X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=src%2Fasm.c;h=3c53096c05956f084497ed8d8d020459a1ff91eb;hb=8e63120c630c94c598d4e2d6ba823dac59bce8fa;hp=6a263a311b6196ec327fcbd6d2cbc72a5ff307e3;hpb=d11f2f726ed7feea617476d99cf7505ddd9a27ce;p=inform.git diff --git a/src/asm.c b/src/asm.c index 6a263a3..3c53096 100644 --- a/src/asm.c +++ b/src/asm.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "asm" : The Inform assembler */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -21,27 +21,32 @@ #include "header.h" -uchar *zcode_holding_area; /* Area holding code yet to be transferred +static uchar *zcode_holding_area; /* Area holding code yet to be transferred to either zcode_area or temp file no 1 */ -uchar *zcode_markers; /* Bytes holding marker values for this +static memory_list zcode_holding_area_memlist; +static uchar *zcode_markers; /* Bytes holding marker values for this code */ +static memory_list zcode_markers_memlist; static int zcode_ha_size; /* Number of bytes in holding area */ -memory_block zcode_area; /* Block to hold assembled code (if - temporary files are not being used) */ +uchar *zcode_area; /* Array to hold assembled code */ + +memory_list zcode_area_memlist; /* Manages zcode_area */ int32 zmachine_pc; /* PC position of assembly (byte offset from start of Z-code area) */ int32 no_instructions; /* Number of instructions assembled */ -int execution_never_reaches_here, /* TRUE if the current PC value in the +int execution_never_reaches_here; /* nonzero if the current PC value in the code area cannot be reached: e.g. if the previous instruction was a "quit" - opcode and no label is set to here */ - next_label, /* Used to count the labels created all + opcode and no label is set to here + (see EXECSTATE flags for more) */ +int next_label, /* Used to count the labels created all over Inform in current routine, from 0 */ next_sequence_point; /* Likewise, for sequence points */ -int no_sequence_points; /* Kept for statistics purposes only */ +int no_sequence_points; /* Total over all routines; kept for + statistics purposes only */ static int label_moved_error_already_given; /* When one label has moved, all subsequent @@ -60,18 +65,17 @@ int uses_acceleration_features; /* Makes use of Glulx acceleration (3.1.1) features? */ int uses_float_features; /* Makes use of Glulx floating-point (3.1.2) features? */ +int uses_extundo_features; /* Makes use of Glulx extended undo (3.1.3) + features? */ debug_location statement_debug_location; /* Location of current statement */ -int32 *variable_tokens; /* The allocated size is - (MAX_LOCAL_VARIABLES + - MAX_GLOBAL_VARIABLES). The entries - MAX_LOCAL_VARIABLES and up give the - symbol table index for the names of - the global variables */ -int *variable_usage; /* TRUE if referred to, FALSE otherwise */ +variableinfo *variables; /* The allocated size is + (MAX_LOCAL_VARIABLES + no_globals). + Local variables first, then globals. */ +memory_list variables_memlist; assembly_instruction AI; /* A structure used to hold the full specification of a single Z-code @@ -85,14 +89,17 @@ static char opcode_syntax_string[128]; /* Text buffer holding the correct static int routine_symbol; /* The symbol index of the routine currently being compiled */ -static char *routine_name; /* The name of the routine currently being - compiled */ +static memory_list current_routine_name; /* The name of the routine currently + being compiled. (This may be longer + than MAX_IDENTIFIER_LENGTH, e.g. for + an "obj.prop" property routine.) */ static int routine_locals; /* The number of local variables used by the routine currently being compiled */ static int32 routine_start_pc; -int32 *named_routine_symbols; +int32 *named_routine_symbols; /* Allocated up to no_named_routines */ +static memory_list named_routine_symbols_memlist; static void transfer_routine_z(void); static void transfer_routine_g(void); @@ -101,34 +108,158 @@ static void transfer_routine_g(void); /* Label data */ /* ------------------------------------------------------------------------- */ +static labelinfo *labels; /* Label offsets (i.e. zmachine_pc values). + These are allocated sequentially, but accessed + as a double-linked list from first_label + to last_label (in PC order). */ +static memory_list labels_memlist; static int first_label, last_label; -static int32 *label_offsets; /* Double-linked list of label offsets */ -static int *label_next, /* (i.e. zmachine_pc values) in PC order */ - *label_prev; -static int32 *label_symbols; /* Symbol numbers if defined in source */ -static int *sequence_point_labels; - /* Label numbers for each */ -static debug_location *sequence_point_locations; - /* Source code references for each */ - /* (used for making debugging file) */ +static int *labeluse; /* Flags indicating whether a given label has been + used as a branch target yet. */ +static memory_list labeluse_memlist; +static int labeluse_size; /* Entries up to here are initialized */ + +static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point. + Only used when debugfile_switch + is set. */ +static memory_list sequence_points_memlist; + +/* ------------------------------------------------------------------------- */ +/* Label management */ +/* ------------------------------------------------------------------------- */ + +/* The stripping of unreachable code requires a bit of explanation. + + As we compile a function, we update the execution_never_reaches_here + flag to reflect whether the current line is reachable. If the flag + is set (EXECSTATE_UNREACHABLE), we skip generating opcodes, + compiling strings, and so on. (See assemblez_instruction(), + assembleg_instruction(), and compile_string() for these checks.) + + If we're *between* functions, the execution_never_reaches_here flag + is always clear (EXECSTATE_REACHABLE), so global strings are + compiled correctly. + + In general, every time we compile a JUMP or RETURN opcode, the flag + gets set. Every time we compile a label, the flag gets cleared. + This makes sense if you consider a common "while" loop: + + while (true) { + ... + if (flag) break; + ... + } + ... + + This is compiled as: + + .TopLabel; + ... + @jnz flag ?ExitLabel; + ... + @jump TopLabel; + .ExitLabel; + ... + + Code after an unconditional JUMP is obviously unreachable. But the + next thing that happens is the .ExitLabel, which is the target of a + branch, so the next line is reachable again. + + However, if the unreachable flag is set when we *begin* a statement + (or braced block of statements), we can get tougher. We set the + EXECSTATE_ENTIRE flag for the entire duration of the statement or + block. This flag cannot be cleared by compiling labels. An example + of this: + + if (DOSTUFF) { + while (true) { + if (flag) break; + } + } + + If the DOSTUFF constant is false, the entire "while" loop is definitely + unreachable. So we should skip .TopLabel, .ExitLabel, and everything + in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the + "if {...}" braced block, and reset it upon leaving. + + (See parse_code_block() and parse_statement() for the (slightly fugly) + bit-fidding that accomplishes this.) + + As an added optimization, some labels are known to be "forward"; they + are only reached by forward jumps. (.ExitLabel above is an example.) + If we reach a forward label and nothing has in fact jumped there, + the label is dead and we can skip it. (And thus also skip clearing + the unreachable flag!) + + To understand *that*, consider a "while true" loop with no "break": + + while (true) { + ... + if (flag) return; + ... + } + + This never branches to .ExitLabel. So when we reach .ExitLabel, + we can say for sure that *nothing* branches there. So we skip + compiling that label. The unreachable flag is left set (because we + just finished the jump to .TopLabel). Thus, any code following the + entire loop is known to be unreachable, and we can righteously + complain about it. + + (In contrast, .TopLabel cannot be skipped because it might be the + target of a *backwards* branch later on. In fact there might be + several -- any "continue" in the loop will jump to .TopLabel.) +*/ + +/* Set the position of the given label. The offset will be the current + zmachine_pc, or -1 if the label is definitely unused. + This adds the label to a linked list (via first_label, last_label). + + The linked list must be in increasing PC order. We know this will + be true because we call this as we run through the function, so + zmachine_pc always increases. +*/ static void set_label_offset(int label, int32 offset) { - if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS); - - label_offsets[label] = offset; + ensure_memory_list_available(&labels_memlist, label+1); + + labels[label].offset = offset; + labels[label].symbol = -1; + if (offset < 0) { + /* Mark this label as invalid and don't put it in the linked list. */ + labels[label].prev = -1; + labels[label].next = -1; + return; + } + if (last_label == -1) - { label_prev[label] = -1; + { labels[label].prev = -1; first_label = label; } else - { label_prev[label] = last_label; - label_next[last_label] = label; + { labels[label].prev = last_label; + labels[last_label].next = label; } last_label = label; - label_next[label] = -1; - label_symbols[label] = -1; + labels[label].next = -1; +} + +/* Set a flag indicating that the given label has been jumped to. */ +static void mark_label_used(int label) +{ + if (label < 0) + return; + + /* Entries from 0 to labeluse_size have meaningful values. + If we have to increase labeluse_size, initialize the new + entries to FALSE. */ + ensure_memory_list_available(&labeluse_memlist, label+1); + for (; labeluse_size < label+1; labeluse_size++) { + labeluse[labeluse_size] = FALSE; + } + labeluse[label] = TRUE; } /* ------------------------------------------------------------------------- */ @@ -187,7 +318,7 @@ extern int is_variable_ot(int otval) extern char *variable_name(int32 i) { if (i==0) return("sp"); - if (imarker) { + printf((!any) ? " (" : ": "); + any = TRUE; + printf("%s", describe_mv(o->marker)); + switch (o->marker) { + case VROUTINE_MV: + printf(": %s", veneer_routine_name(o->value)); + break; + case INCON_MV: + printf(": %s", name_of_system_constant(o->value)); + break; + case DWORD_MV: + printf(": '"); + print_dict_word(o->value); + printf("'"); + break; + } + } + if (o->symindex >= 0 && o->symindex < no_symbols) { + printf((!any) ? " (" : ": "); + any = TRUE; + printf("%s", symbols[o->symindex].name); + } + if (any) printf(")"); +} + +static void print_operand_z(const assembly_operand *o, int annotate) +{ switch(o->type) { case EXPRESSION_OT: printf("expr_"); break; case LONG_CONSTANT_OT: printf("long_"); break; case SHORT_CONSTANT_OT: printf("short_"); break; case VARIABLE_OT: - if (o.value==0) { printf("sp"); return; } - printf("%s", variable_name(o.value)); return; + if (o->value==0) { printf("sp"); return; } + printf("%s", variable_name(o->value)); return; case OMITTED_OT: printf(""); return; } - printf("%d", o.value); + printf("%d", o->value); + if (annotate) + print_operand_annotation(o); } -static void print_operand_g(assembly_operand o) +static void print_operand_g(const assembly_operand *o, int annotate) { - switch (o.type) { + switch (o->type) { case EXPRESSION_OT: printf("expr_"); break; case CONSTANT_OT: printf("long_"); break; case HALFCONSTANT_OT: printf("short_"); break; @@ -244,32 +407,34 @@ static void print_operand_g(assembly_operand o) case ZEROCONSTANT_OT: printf("zero_"); return; case DEREFERENCE_OT: printf("*"); break; case GLOBALVAR_OT: - printf("%s (global_%d)", variable_name(o.value), o.value); + printf("global_%d (%s)", o->value, variable_name(o->value)); return; case LOCALVAR_OT: - if (o.value == 0) + if (o->value == 0) printf("stackptr"); else - printf("%s (local_%d)", variable_name(o.value), o.value-1); + printf("local_%d (%s)", o->value-1, variable_name(o->value)); return; case SYSFUN_OT: - if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS) - printf("%s", system_functions.keywords[o.value]); + if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS) + printf("%s", system_functions.keywords[o->value]); else printf(""); return; case OMITTED_OT: printf(""); return; default: printf("???_"); break; } - printf("%d", o.value); + printf("%d", o->value); + if (annotate) + print_operand_annotation(o); } -extern void print_operand(assembly_operand o) +extern void print_operand(const assembly_operand *o, int annotate) { if (!glulx_mode) - print_operand_z(o); + print_operand_z(o, annotate); else - print_operand_g(o); + print_operand_g(o, annotate); } /* ------------------------------------------------------------------------- */ @@ -277,8 +442,9 @@ extern void print_operand(assembly_operand o) /* ------------------------------------------------------------------------- */ static void byteout(int32 i, int mv) -{ if (zcode_ha_size >= MAX_ZCODE_SIZE) - memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE); +{ + ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1); + ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1); zcode_markers[zcode_ha_size] = (uchar) mv; zcode_holding_area[zcode_ha_size++] = (uchar) i; zmachine_pc++; @@ -286,7 +452,7 @@ static void byteout(int32 i, int mv) /* ------------------------------------------------------------------------- */ /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */ -/* And of the however-many-there-are Glulx opcode */ +/* And of the however-many-there-are Glulx opcodes */ /* ------------------------------------------------------------------------- */ typedef struct opcodez @@ -337,6 +503,7 @@ typedef struct opcodeg #define GOP_MemHeap 2 /* uses_memheap_features */ #define GOP_Acceleration 4 /* uses_acceleration_features */ #define GOP_Float 8 /* uses_float_features */ +#define GOP_ExtUndo 16 /* uses_extundo_features */ /* Codes for the number of operands */ @@ -634,6 +801,8 @@ static opcodeg opcodes_table_g[] = { { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 }, { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 }, { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 }, + { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 }, + { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 }, }; /* The opmacros table is used for fake opcodes. The opcode numbers are @@ -792,9 +961,10 @@ static void write_operand(assembly_operand op) } } -extern void assemblez_instruction(assembly_instruction *AI) +extern void assemblez_instruction(const assembly_instruction *AI) { - uchar *start_pc, *operands_pc; + int32 operands_pc; + int32 start_pc; int32 offset, j, topbits=0, types_byte1, types_byte2; int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE; assembly_operand o1, o2; @@ -802,6 +972,15 @@ extern void assemblez_instruction(assembly_instruction *AI) ASSERT_ZCODE(); + if (execution_never_reaches_here) { + if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) { + warning("This statement can never be reached"); + /* only show the warning once */ + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + return; + } + offset = zmachine_pc; no_instructions++; @@ -810,8 +989,10 @@ extern void assemblez_instruction(assembly_instruction *AI) if (sequence_point_follows) { sequence_point_follows = FALSE; at_seq_point = TRUE; if (debugfile_switch) - { sequence_point_labels[next_sequence_point] = next_label; - sequence_point_locations[next_sequence_point] = + { + ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1); + sequence_points[next_sequence_point].label = next_label; + sequence_points[next_sequence_point].location = statement_debug_location; set_label_offset(next_label++, zmachine_pc); } @@ -825,11 +1006,8 @@ extern void assemblez_instruction(assembly_instruction *AI) return; } - if (execution_never_reaches_here) - warning("This statement can never be reached"); - operand_rules = opco.op_rules; - execution_never_reaches_here = ((opco.flags & Rf) != 0); + execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE); if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1; @@ -840,7 +1018,7 @@ extern void assemblez_instruction(assembly_instruction *AI) /* 1. Write the opcode byte(s) */ - start_pc = zcode_holding_area + zcode_ha_size; + start_pc = zcode_ha_size; switch(opco.no) { case VAR_LONG: topbits=0xc0; min=0; max=8; break; @@ -855,23 +1033,31 @@ extern void assemblez_instruction(assembly_instruction *AI) } byteout(opco.code + topbits, 0); - operands_pc = zcode_holding_area + zcode_ha_size; + operands_pc = zcode_ha_size; /* 2. Dispose of the special rules LABEL and TEXT */ if (operand_rules==LABEL) { j = (AI->operand[0]).value; + mark_label_used(j); byteout(j/256, LABEL_MV); byteout(j%256, 0); goto Instruction_Done; } if (operand_rules==TEXT) { int32 i; - uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text, STRCTX_GAMEOPC); - if (!tmp) - memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE); - j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size)); - for (i=0; itext, STRCTX_GAMEOPC); + if (j < 0) { + error("text translation failed"); + j = 0; + } + ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j); + ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j); + for (i=0; ioperand[0]; - *start_pc=(*start_pc) + o1.type*0x10; + zcode_holding_area[start_pc] += o1.type*0x10; write_operand(o1); break; @@ -919,12 +1105,12 @@ extern void assemblez_instruction(assembly_instruction *AI) /* Transfer to VAR form if either operand is a long constant */ if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT)) - { *start_pc=(*start_pc) + 0xc0; + { zcode_holding_area[start_pc] += 0xc0; byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0); } else - { if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40; - if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20; + { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40; + if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20; } write_operand(o1); write_operand(o2); @@ -934,12 +1120,12 @@ extern void assemblez_instruction(assembly_instruction *AI) /* 4. Assemble a Store destination, if needed */ if ((AI->store_variable_number) != -1) - { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) { + { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) { goto OpcodeSyntaxError; } o1.type = VARIABLE_OT; o1.value = AI->store_variable_number; - variable_usage[o1.value] = TRUE; + variables[o1.value].usage = TRUE; o1.marker = 0; /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239) @@ -956,7 +1142,7 @@ extern void assemblez_instruction(assembly_instruction *AI) if (AI->branch_label_number != -1) { int32 addr, long_form; int branch_on_true = (AI->branch_flag)?1:0; - + mark_label_used(AI->branch_label_number); switch (AI->branch_label_number) { case -2: addr = 2; branch_on_true = 0; long_form = 0; break; /* branch nowhere, carry on */ @@ -994,7 +1180,7 @@ extern void assemblez_instruction(assembly_instruction *AI) for (i=0; ioperand_count; i++) { if ((i==0) && (opco.op_rules == VARIAB)) { if ((AI->operand[0]).type == VARIABLE_OT) - { printf("["); print_operand_z(AI->operand[i]); } + { printf("["); print_operand_z(&AI->operand[i], TRUE); } else printf("%s", variable_name((AI->operand[0]).value)); } @@ -1002,14 +1188,14 @@ extern void assemblez_instruction(assembly_instruction *AI) if ((i==0) && (opco.op_rules == LABEL)) { printf("L%d", AI->operand[0].value); } - else print_operand_z(AI->operand[i]); + else print_operand_z(&AI->operand[i], TRUE); printf(" "); } if (AI->store_variable_number != -1) { assembly_operand AO; printf("-> "); AO.type = VARIABLE_OT; AO.value = AI->store_variable_number; - print_operand_z(AO); printf(" "); + print_operand_z(&AO, TRUE); printf(" "); } switch(AI->branch_label_number) @@ -1025,10 +1211,10 @@ extern void assemblez_instruction(assembly_instruction *AI) } if (asm_trace_level>=2) - { for (j=0;start_pcinternal_number); - if (execution_never_reaches_here) - warning("This statement can never be reached"); - - execution_never_reaches_here = ((opco.flags & Rf) != 0); + execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE); if (opco.op_rules & GOP_Unicode) { uses_unicode_features = TRUE; @@ -1143,12 +1338,15 @@ extern void assembleg_instruction(assembly_instruction *AI) if (opco.op_rules & GOP_Float) { uses_float_features = TRUE; } + if (opco.op_rules & GOP_ExtUndo) { + uses_extundo_features = TRUE; + } no_operands_given = AI->operand_count; /* 1. Write the opcode byte(s) */ - start_pc = zcode_holding_area + zcode_ha_size; + start_pc = zcode_ha_size; if (opco.code < 0x80) { byteout(opco.code, 0); @@ -1168,7 +1366,7 @@ extern void assembleg_instruction(assembly_instruction *AI) every two operands (rounded up). We write zeroes for now; when the operands are written, we'll go back and fix them. */ - opmodes_pc = zcode_holding_area + zcode_ha_size; + opmodes_pc = zcode_ha_size; for (ix=0; ix= BRANCH_MV && marker < BRANCHMAX_MV)) { @@ -1340,7 +1538,7 @@ extern void assembleg_instruction(assembly_instruction *AI) if (ix & 1) j = (j << 4); - opmodes_pc[ix/2] |= j; + zcode_holding_area[opmodes_pc+ix/2] |= j; } /* Print assembly trace. */ @@ -1359,23 +1557,23 @@ extern void assembleg_instruction(assembly_instruction *AI) printf("to L%d", AI->operand[i].value); } else { - print_operand_g(AI->operand[i]); + print_operand_g(&AI->operand[i], TRUE); } printf(" "); } if (asm_trace_level>=2) { for (j=0; - start_pc 0) printf("%5d +%05lx .L%d\n", ErrorReport.line_number, ((long int) zmachine_pc), n); set_label_offset(n, zmachine_pc); - execution_never_reaches_here = FALSE; + execution_never_reaches_here = EXECSTATE_REACHABLE; +} + +/* This is the same as assemble_label_no, except we only set up the label + if there has been a forward branch to it. + Returns whether the label is created. + Only use this for labels which never have backwards branches! +*/ +extern int assemble_forward_label_no(int n) +{ + if (n >= 0 && n < labeluse_size && labeluse[n]) { + assemble_label_no(n); + return TRUE; + } + else { + /* There were no forward branches to this label and we promise + there will be no backwards branches to it. Set a negative + offset, which will trip an error if we break our promise. */ + set_label_offset(n, -1); + return FALSE; + } } extern void define_symbol_label(int symbol) -{ label_symbols[svals[symbol]] = symbol; +{ + int label = symbols[symbol].value; + /* We may be creating a new label (label = next_label) or filling in + the value of an old one. So we call ensure. */ + ensure_memory_list_available(&labels_memlist, label+1); + labels[label].symbol = symbol; } extern int32 assemble_routine_header(int no_locals, @@ -1412,13 +1650,15 @@ extern int32 assemble_routine_header(int no_locals, int stackargs = FALSE; int name_length; - execution_never_reaches_here = FALSE; + execution_never_reaches_here = EXECSTATE_REACHABLE; routine_locals = no_locals; - for (i=0; i= 1 - && !strcmp(local_variables.keywords[0], "_vararg_count")) { + if (no_locals >= 1 + && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) { stackargs = TRUE; } @@ -1448,9 +1688,8 @@ extern int32 assemble_routine_header(int no_locals, routine_symbol = the_symbol; name_length = strlen(name) + 1; - routine_name = - my_malloc(name_length * sizeof(char), "temporary copy of routine name"); - strncpy(routine_name, name, name_length); + ensure_memory_list_available(¤t_routine_name, name_length); + strncpy(current_routine_name.data, name, name_length); /* Update the routine counter */ @@ -1476,6 +1715,7 @@ extern int32 assemble_routine_header(int no_locals, for (i=0; i"); if (embedded_flag) { debug_file_printf ("%s", routine_name); } - else if (sflags[routine_symbol] & REPLACE_SFLAG) + else if (symbols[routine_symbol].flags & REPLACE_SFLAG) { /* The symbol type will be set to ROUTINE_T once the replaced version has been given; if it is already set, we must be dealing with a replacement, and we can use the routine name as-is. Otherwise we look for a rename. And if that doesn't work, we fall back to an artificial identifier. */ - if (stypes[routine_symbol] == ROUTINE_T) + if (symbols[routine_symbol].type == ROUTINE_T) { /* Optional because there may be further replacements. */ write_debug_optional_identifier(routine_symbol); } else if (find_symbol_replacement(&routine_symbol)) { debug_file_printf - ("%s", symbs[routine_symbol]); + ("%s", symbols[routine_symbol].name); } else { debug_file_printf @@ -1737,67 +1982,61 @@ void assemble_routine_end(int embedded_flag, debug_locations locations) { debug_file_printf(""); debug_file_printf("
"); write_debug_code_backpatch - (label_offsets[sequence_point_labels[i]]); + (labels[sequence_points[i].label].offset); debug_file_printf("
"); - write_debug_location(sequence_point_locations[i]); + write_debug_location(sequence_points[i].location); debug_file_printf("
"); } debug_file_printf(""); } - my_free(&routine_name, "temporary copy of routine name"); - /* Issue warnings about any local variables not used in the routine. */ for (i=1; i<=routine_locals; i++) - if (!(variable_usage[i])) + if (!(variables[i].usage)) dbnu_warning("Local variable", variable_name(i), routine_starts_line); for (i=0; i= 4) - printf("To label %d, which is %d from here\n", - j, label_offsets[j]-pc); - if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64)) - { if (asm_trace_level >= 4) printf("Short form\n"); + printf("...To label %d, which is %d from here\n", + j, labels[j].offset-pc); + if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64)) + { if (asm_trace_level >= 4) printf("...Using short form\n"); + zcode_markers[i+1] = DELETED_MV; + } + } + else if (zcode_markers[i] == LABEL_MV) + { + if (asm_trace_level >= 4) + printf("Jump detected at offset %04x\n", pc); + j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff; + if (asm_trace_level >= 4) + printf("...To label %d, which is %d from here\n", + j, labels[j].offset-pc); + if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) { + if (asm_trace_level >= 4) printf("...Deleting jump\n"); + zcode_markers[i-1] = DELETED_MV; + zcode_markers[i] = DELETED_MV; zcode_markers[i+1] = DELETED_MV; } } @@ -1841,17 +2095,18 @@ static void transfer_routine_z(void) { printf("Opening label: %d\n", first_label); for (i=0;i %d previous -> %d\n", - i, label_offsets[i], label_next[i], label_prev[i]); + i, labels[i].offset, labels[i].next, labels[i].prev); } + /* label will advance through the linked list as pc increases. */ for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label; i= 4) printf("Position of L%d corrected from %04x to %04x\n", - label, label_offsets[label], new_pc); - label_offsets[label] = new_pc; - label = label_next[label]; + label, labels[label].offset, new_pc); + labels[label].offset = new_pc; + label = labels[label].next; } if (zcode_markers[i] != DELETED_MV) new_pc++; } @@ -1861,6 +2116,8 @@ static void transfer_routine_z(void) operands with offsets to those labels. Also issue markers, now that we know where they occur in the final Z-code area. */ + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size); + for (i=0, new_pc=adjusted_pc; i0x1fff) fatalerror("Branch out of range: divide the routine up?"); if (addr<0) addr+=(int32) 0x10000L; @@ -1887,18 +2150,24 @@ static void transfer_routine_z(void) } zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f); } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; case LABEL_MV: j = 256*zcode_holding_area[i] + zcode_holding_area[i+1]; - addr = label_offsets[j] - new_pc; + if (labels[j].offset < 0) { + error("Attempt to jump to an unreachable label"); + addr = 0; + } + else { + addr = labels[j].offset - new_pc; + } if (addr<-0x8000 || addr>0x7fff) fatalerror("Jump out of range: divide the routine up?"); if (addr<0) addr += (int32) 0x10000L; zcode_holding_area[i] = addr/256; zcode_holding_area[i+1] = addr%256; - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; case DELETED_MV: @@ -1920,20 +2189,26 @@ static void transfer_routine_z(void) break; } - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - zcode_markers[i] + 32*(new_pc/65536)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, (new_pc/256)%256); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, new_pc%256); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc); + + ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3); + zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536); + zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256; + zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256; break; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; } } + /* Consistency check */ + if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc) + { + fatalerror("Optimisation increased routine length or failed to match; should not happen"); + } + if (asm_trace_level >= 3) { printf("After branch optimisation, routine length is %d bytes\n", new_pc - rstart_pc); @@ -1942,13 +2217,12 @@ static void transfer_routine_z(void) /* Insert null bytes if necessary to ensure the next routine address is */ /* expressible as a packed address */ - { uchar zero[1]; - zero[0] = 0; - if (oddeven_packing_switch) - while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero); - else - while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero); - } + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor); + + if (oddeven_packing_switch) + while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0; + else + while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0; zmachine_pc = adjusted_pc; zcode_ha_size = 0; @@ -1957,7 +2231,7 @@ static void transfer_routine_z(void) static void transfer_routine_g(void) { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr, rstart_pc; - void (* transfer_byte)(uchar *); + int32 adjusted_pc; adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc; @@ -1966,12 +2240,12 @@ static void transfer_routine_g(void) (long int) adjusted_pc, zcode_ha_size, next_label); } - transfer_byte = - (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area; - /* (1) Scan through for branches and make short/long decisions in each case. Mark omitted bytes (bytes 2-4 in branches converted to - short form) with DELETED_MV. */ + short form) with DELETED_MV. + We also look for branches that can be entirely eliminated (because + they are jumping to the very next instruction). The opcode and + all label bytes get DELETED_MV. */ for (i=0, pc=adjusted_pc; i= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) { @@ -1984,16 +2258,25 @@ static void transfer_routine_g(void) | (zcode_holding_area[i+2] << 8) | (zcode_holding_area[i+3])); offset_of_next = pc + 4; - addr = (label_offsets[j] - offset_of_next) + 2; + addr = (labels[j].offset - offset_of_next) + 2; + opmodebyte = i - ((opmodeoffset+1)/2); if (asm_trace_level >= 4) - printf("To label %d, which is (%d-2) = %d from here\n", - j, addr, label_offsets[j] - offset_of_next); - if (addr >= -0x80 && addr < 0x80) { + printf("...To label %d, which is (%d-2) = %d from here\n", + j, addr, labels[j].offset - offset_of_next); + if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) { + if (asm_trace_level >= 4) printf("...Deleting branch\n"); + zcode_markers[i-2] = DELETED_MV; + zcode_markers[i-1] = DELETED_MV; + zcode_markers[i] = DELETED_MV; + zcode_markers[i+1] = DELETED_MV; + zcode_markers[i+2] = DELETED_MV; + zcode_markers[i+3] = DELETED_MV; + } + else if (addr >= -0x80 && addr < 0x80) { if (asm_trace_level >= 4) printf("...Byte form\n"); zcode_markers[i+1] = DELETED_MV; zcode_markers[i+2] = DELETED_MV; zcode_markers[i+3] = DELETED_MV; - opmodebyte = i - ((opmodeoffset+1)/2); if ((opmodeoffset & 1) == 0) zcode_holding_area[opmodebyte] = (zcode_holding_area[opmodebyte] & 0xF0) | 0x01; @@ -2005,7 +2288,6 @@ static void transfer_routine_g(void) if (asm_trace_level >= 4) printf("...Short form\n"); zcode_markers[i+2] = DELETED_MV; zcode_markers[i+3] = DELETED_MV; - opmodebyte = i - ((opmodeoffset+1)/2); if ((opmodeoffset & 1) == 0) zcode_holding_area[opmodebyte] = (zcode_holding_area[opmodebyte] & 0xF0) | 0x02; @@ -2028,18 +2310,19 @@ static void transfer_routine_g(void) printf("Opening label: %d\n", first_label); for (i=0;i %d previous -> %d\n", - i, label_offsets[i], label_next[i], label_prev[i]); + i, labels[i].offset, labels[i].next, labels[i].prev); } + /* label will advance through the linked list as pc increases. */ for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label; i= 4) printf("Position of L%d corrected from %04x to %04x\n", - label, label_offsets[label], new_pc); - label_offsets[label] = new_pc; - label = label_next[label]; + label, labels[label].offset, new_pc); + labels[label].offset = new_pc; + label = labels[label].next; } if (zcode_markers[i] != DELETED_MV) new_pc++; } @@ -2049,6 +2332,8 @@ static void transfer_routine_g(void) operands with offsets to those labels. Also issue markers, now that we know where they occur in the final Z-code area. */ + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size); + for (i=0, new_pc=adjusted_pc; i= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) { @@ -2070,7 +2355,13 @@ static void transfer_routine_g(void) after it. */ offset_of_next = new_pc + form_len; - addr = (label_offsets[j] - offset_of_next) + 2; + if (labels[j].offset < 0) { + error("Attempt to jump to an unreachable label"); + addr = 0; + } + else { + addr = (labels[j].offset - offset_of_next) + 2; + } if (asm_trace_level >= 4) { printf("Branch at offset %04x: %04x (%s)\n", new_pc, addr, ((form_len == 1) ? "byte" : @@ -2095,7 +2386,7 @@ static void transfer_routine_g(void) zcode_holding_area[i+2] = (addr >> 8) & 0xFF; zcode_holding_area[i+3] = (addr) & 0xFF; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; } else if (zcode_markers[i] == LABEL_MV) { error("*** No LABEL opcodes in Glulx ***"); @@ -2124,26 +2415,27 @@ static void transfer_routine_g(void) Then a byte indicating the data size to be patched (1, 2, 4). Then the four-byte address (new_pc). */ - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - zcode_markers[i]); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - 4); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 24) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 16) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 8) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, (new_pc & 0xFF)); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc); + ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6); + zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i]; + zcode_backpatch_table[zcode_backpatch_size++] = 4; + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF); break; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; } } + /* Consistency check */ + if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc) + { + fatalerror("Optimisation increased routine length or failed to match; should not happen"); + } + if (asm_trace_level >= 3) { printf("After branch optimisation, routine length is %d bytes\n", new_pc - rstart_pc); @@ -2213,7 +2505,22 @@ void assemblez_1_to(int internal_number, void assemblez_1_branch(int internal_number, assembly_operand o1, int label, int flag) -{ AI.internal_number = internal_number; +{ + /* Some clever optimizations first. A constant is always or never equal + to zero. */ + if (o1.marker == 0 && is_constant_ot(o1.type)) { + if (internal_number == jz_zc) { + if ((flag && o1.value == 0) || (!flag && o1.value != 0)) { + assemblez_jump(label); + return; + } + else { + /* assemble nothing at all! */ + return; + } + } + } + AI.internal_number = internal_number; AI.operand_count = 1; AI.operand[0] = o1; AI.branch_label_number = label; @@ -2563,9 +2870,6 @@ void assembleg_1_branch(int internal_number, if ((internal_number == jz_gc && o1.value == 0) || (internal_number == jnz_gc && o1.value != 0)) { assembleg_0_branch(jump_gc, label); - /* We clear the "can't reach statement" flag here, - so that "if (1)" doesn't produce that warning. */ - execution_never_reaches_here = 0; return; } if ((internal_number == jz_gc && o1.value != 0) @@ -2778,9 +3082,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)"); get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { assemblez_instruction(&AI); + AI.text = NULL; return; } ebf_error("semicolon ';' after print string", token_text); + AI.text = NULL; put_token_back(); return; } @@ -2803,11 +3109,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)"); else { if (strcmp(token_text, "sp") == 0) n = 0; else - { if (stypes[token_value] != GLOBAL_VARIABLE_T) + { if (symbols[token_value].type != GLOBAL_VARIABLE_T) error_named( "Store '->' destination not 'sp' or a variable:", token_text); - else n = svals[token_value]; + else n = symbols[token_value].value; } } AI.store_variable_number = n; @@ -2961,6 +3267,7 @@ static void parse_assembly_g(void) int error_flag = FALSE, is_macro = FALSE; AI.operand_count = 0; + AI.text = NULL; opcode_names.enabled = TRUE; opcode_macros.enabled = TRUE; @@ -3114,15 +3421,6 @@ extern void parse_assembly(void) /* Data structure management routines */ /* ------------------------------------------------------------------------- */ -extern void asm_begin_pass(void) -{ no_instructions = 0; - zmachine_pc = 0; - no_sequence_points = 0; - next_label = 0; - next_sequence_point = 0; - zcode_ha_size = 0; -} - extern void init_asm_vars(void) { int i; @@ -3132,56 +3430,76 @@ extern void init_asm_vars(void) uses_memheap_features = FALSE; uses_acceleration_features = FALSE; uses_float_features = FALSE; + uses_extundo_features = FALSE; + labels = NULL; + sequence_points = NULL; sequence_point_follows = TRUE; label_moved_error_already_given = FALSE; - initialise_memory_block(&zcode_area); + zcode_area = NULL; } -extern void asm_allocate_arrays(void) -{ if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000; - - variable_tokens = my_calloc(sizeof(int32), - MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens"); - variable_usage = my_calloc(sizeof(int), - MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage"); - - label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets"); - label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols"); - label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1"); - label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1"); - sequence_point_labels - = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels"); - sequence_point_locations - = my_calloc(sizeof(debug_location), - MAX_LABELS, - "sequence point locations"); - - zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area"); - zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area"); +extern void asm_begin_pass(void) +{ no_instructions = 0; + zmachine_pc = 0; + no_sequence_points = 0; + next_label = 0; + labeluse_size = 0; + next_sequence_point = 0; + zcode_ha_size = 0; + execution_never_reaches_here = EXECSTATE_REACHABLE; +} - named_routine_symbols - = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols"); +extern void asm_allocate_arrays(void) +{ + initialise_memory_list(&variables_memlist, + sizeof(variableinfo), 200, (void**)&variables, + "variables"); + + initialise_memory_list(&labels_memlist, + sizeof(labelinfo), 1000, (void**)&labels, + "labels"); + initialise_memory_list(&labeluse_memlist, + sizeof(int), 1000, (void**)&labeluse, + "labeluse"); + initialise_memory_list(&sequence_points_memlist, + sizeof(sequencepointinfo), 1000, (void**)&sequence_points, + "sequence points"); + + initialise_memory_list(&zcode_holding_area_memlist, + sizeof(uchar), 2000, (void**)&zcode_holding_area, + "compiled routine code area"); + initialise_memory_list(&zcode_markers_memlist, + sizeof(uchar), 2000, (void**)&zcode_markers, + "compiled routine markers area"); + + initialise_memory_list(&named_routine_symbols_memlist, + sizeof(int32), 1000, (void**)&named_routine_symbols, + "named routine symbols"); + + initialise_memory_list(&zcode_area_memlist, + sizeof(uchar), 8192, (void**)&zcode_area, + "code area"); + + initialise_memory_list(¤t_routine_name, + sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL, + "routine name currently being defined"); } extern void asm_free_arrays(void) { - my_free(&variable_tokens, "variable tokens"); - my_free(&variable_usage, "variable usage"); + deallocate_memory_list(&variables_memlist); - my_free(&label_offsets, "label offsets"); - my_free(&label_symbols, "label symbols"); - my_free(&label_next, "label dll 1"); - my_free(&label_prev, "label dll 2"); - my_free(&sequence_point_labels, "sequence point labels"); - my_free(&sequence_point_locations, "sequence point locations"); + deallocate_memory_list(&labels_memlist); + deallocate_memory_list(&sequence_points_memlist); - my_free(&zcode_holding_area, "compiled routine code area"); - my_free(&zcode_markers, "compiled routine code markers"); + deallocate_memory_list(&zcode_holding_area_memlist); + deallocate_memory_list(&zcode_markers_memlist); - my_free(&named_routine_symbols, "named routine symbols"); - deallocate_memory_block(&zcode_area); + deallocate_memory_list(&named_routine_symbols_memlist); + deallocate_memory_list(&zcode_area_memlist); + deallocate_memory_list(¤t_routine_name); } /* ========================================================================= */