To generate final code from intermediate code.
void CodeGen::create_pipeline_stage(void) { CodeGen::Stage::new(I"generate", CodeGen::run_pipeline_stage, TEXT_OUT_STAGE_ARG, FALSE); } int CodeGen::run_pipeline_stage(pipeline_step *step) { if (step->target_argument == NULL) internal_error("no target specified"); inter_package *which = NULL; if (Str::len(step->package_argument) > 0) { which = Inter::Packages::by_url(step->repository, step->package_argument); if (which == NULL) { LOG("Arg %S\n", step->package_argument); internal_error("no such package name"); } } code_generation *gen = CodeGen::new_generation(step, step->repository, which, step->target_argument); if (CodeGen::Targets::begin_generation(gen) == FALSE) { CodeGen::generate(gen); CodeGen::Targets::end_generation(gen); CodeGen::write(step->text_out_file, gen); } return TRUE; }
§2. Generations. A "generation" is a single act of translating inter code into final code. That final code will be a text file written in some other programming language, though probably a low-level one.
The "target" of a generation is the final language: for example, Inform 6.
During a generation, textual output is assembled as a set of "segments". Different targets may need different segments. This is all to facilitate rearranging content as necessary to get it to compile in the target language: for example, one might need to have all constants defined first, then all arrays, and one could do this by creating two segments, one to accumulate the constants in, one to accumulate the arrays.
At any given time, a generation has a "current" segment, to which output is being written. Ome segment is special: the temporary one, which is used only when assembling other material, and not for the final output.
enum temporary_I7CGS from 0 define MAX_CG_SEGMENTS 100
typedef struct code_generation { struct pipeline_step *from_step; struct inter_tree *from; struct code_generation_target *target; struct inter_package *just_this_package; struct generated_segment *segments[MAX_CG_SEGMENTS]; struct linked_list *segment_sequence; of generated_segment struct generated_segment *current_segment; an entry in that array, or null int temporarily_diverted; to the temporary segment void *target_specific_data; depending on the target generated to CLASS_DEFINITION } code_generation; code_generation *CodeGen::new_generation(pipeline_step *step, inter_tree *I, inter_package *just, code_generation_target *target) { code_generation *gen = CREATE(code_generation); gen->from_step = step; gen->from = I; gen->target = target; if (just) gen->just_this_package = just; else gen->just_this_package = Site::main_package(I); gen->current_segment = NULL; gen->segment_sequence = NEW_LINKED_LIST(generated_segment); gen->temporarily_diverted = FALSE; for (int i=0; i<MAX_CG_SEGMENTS; i++) gen->segments[i] = NULL; return gen; }
- The structure code_generation is accessed in 2/fc, 2/cal, 2/iap, 2/vrb, 2/ft, 3/fti, 3/fbi, 3/fi, 4/fi6, 5/fnc, 5/cnm, 5/cmm, 5/cpc, 5/ccn, 5/com and here.
§3. At present, at least, a "segment" is nothing more than a wrapper for a text. But we abstract it in case it's ever useful for it to be more.
typedef struct generated_segment { struct text_stream *generated_code; CLASS_DEFINITION } generated_segment; generated_segment *CodeGen::new_segment(void) { generated_segment *seg = CREATE(generated_segment); seg->generated_code = Str::new(); return seg; } void CodeGen::create_segments(code_generation *gen, void *data, int codes[]) { gen->segment_sequence = NEW_LINKED_LIST(generated_segment); for (int i=0; codes[i] >= 0; i++) { if ((codes[i] >= MAX_CG_SEGMENTS) || (codes[i] == temporary_I7CGS)) internal_error("bad segment sequence"); gen->segments[codes[i]] = CodeGen::new_segment(); ADD_TO_LINKED_LIST(gen->segments[codes[i]], generated_segment, gen->segment_sequence); } gen->target_specific_data = data; }
- The structure generated_segment is private to this section.
§4. And then all we do is concatenate them in order:
void CodeGen::write(OUTPUT_STREAM, code_generation *gen) { generated_segment *seg; LOOP_OVER_LINKED_LIST(seg, generated_segment, gen->segment_sequence) WRITE("%S", seg->generated_code); }
§5. Here we switch the output, by changing the segment selection. This must always be done in a way which is then undone, restoring the previous state:
generated_segment *CodeGen::select(code_generation *gen, int i) { generated_segment *saved = gen->current_segment; if ((i < 0) || (i >= MAX_CG_SEGMENTS)) internal_error("out of range"); if (gen->temporarily_diverted) internal_error("poorly timed selection"); gen->current_segment = gen->segments[i]; return saved; } void CodeGen::deselect(code_generation *gen, generated_segment *saved) { if (gen->temporarily_diverted) internal_error("poorly timed deselection"); gen->current_segment = saved; }
§6. The procedure for selecting the temporary segment is different, because we also have to direct it to a given text.
void CodeGen::select_temporary(code_generation *gen, text_stream *T) { if (gen->segments[temporary_I7CGS] == NULL) { gen->segments[temporary_I7CGS] = CodeGen::new_segment(); gen->segments[temporary_I7CGS]->generated_code = NULL; } if (gen->temporarily_diverted) internal_error("nested temporary cgs"); gen->temporarily_diverted = TRUE; gen->segments[temporary_I7CGS]->generated_code = T; } void CodeGen::deselect_temporary(code_generation *gen) { gen->temporarily_diverted = FALSE; }
§7. Note that temporary selections take precedence over the regular selection.
text_stream *CodeGen::current(code_generation *gen) { if (gen->temporarily_diverted) return gen->segments[temporary_I7CGS]->generated_code; if (gen->current_segment == NULL) return NULL; return gen->current_segment->generated_code; }
§8. Actual generation happens in three phases:
void CodeGen::generate(code_generation *gen) { Phase one - preparation8.1; Phase two - traverse8.2; Phase three - consolidation8.3; }
§8.1. Phase one - preparation8.1 =
InterTree::traverse(gen->from, CodeGen::clear_transients, NULL, NULL, PACKAGE_IST); CodeGen::FC::prepare(gen); CodeGen::CL::prepare(gen); CodeGen::Var::prepare(gen); CodeGen::IP::prepare(gen);
- This code is used in §8.
§8.2. Phase two - traverse8.2 =
InterTree::traverse_root_only(gen->from, CodeGen::pragma, gen, PRAGMA_IST); InterTree::traverse(gen->from, CodeGen::FC::pre_iterate, gen, NULL, -PACKAGE_IST); InterTree::traverse(gen->from, CodeGen::FC::iterate, gen, NULL, -PACKAGE_IST);
- This code is used in §8.
§8.3. Phase three - consolidation8.3 =
CodeGen::IP::write_properties(gen); CodeGen::CL::sort_literals(gen);
- This code is used in §8.
void CodeGen::pragma(inter_tree *I, inter_tree_node *P, void *state) { code_generation *gen = (code_generation *) state; inter_symbol *target_symbol = InterSymbolsTables::symbol_from_frame_data(P, TARGET_PRAGMA_IFLD); if (target_symbol == NULL) internal_error("bad pragma"); inter_ti ID = P->W.data[TEXT_PRAGMA_IFLD]; text_stream *S = Inode::ID_to_text(P, ID); CodeGen::Targets::offer_pragma(gen, P, target_symbol->symbol_name, S); }
§10. Marking. We use a transient flag on symbols, but abstract that here:
int CodeGen::marked(inter_symbol *symb_name) { return Inter::Symbols::get_flag(symb_name, TRAVERSE_MARK_BIT); } void CodeGen::mark(inter_symbol *symb_name) { Inter::Symbols::set_flag(symb_name, TRAVERSE_MARK_BIT); } void CodeGen::unmark(inter_symbol *symb_name) { Inter::Symbols::clear_flag(symb_name, TRAVERSE_MARK_BIT); } void CodeGen::clear_transients(inter_tree *I, inter_tree_node *P, void *state) { inter_package *pack = Inter::Package::defined_by_frame(P); inter_symbols_table *T = Inter::Packages::scope(pack); for (int i=0; i<T->size; i++) if (T->symbol_array[i]) Inter::Symbols::clear_transient_flags(T->symbol_array[i]); }