--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* "symbols" : The symbols table; creating stock of reserved words */
+/* */
+/* Copyright (c) Graham Nelson 1993 - 2018 */
+/* */
+/* This file is part of Inform. */
+/* */
+/* Inform is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Inform is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Inform. If not, see https://gnu.org/licenses/ */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+/* ------------------------------------------------------------------------- */
+/* This section of Inform is a service detached from the rest. */
+/* Only two variables are accessible from the outside: */
+/* ------------------------------------------------------------------------- */
+
+int no_symbols; /* Total number of symbols defined */
+int no_named_constants; /* Copied into story file */
+
+/* ------------------------------------------------------------------------- */
+/* Plus six arrays. Each symbol has its own index n (an int32) and */
+/* */
+/* svals[n] is its value (must be 32 bits wide, i.e. an int32, tho' */
+/* it is used to hold an unsigned 16 bit Z-machine value) */
+/* sflags[n] holds flags (see "header.h" for a list) */
+/* stypes[n] is the "type", distinguishing between the data type of */
+/* different kinds of constants/variables. */
+/* (See the "typename()" below.) */
+/* symbs[n] (needs to be cast to (char *) to be used) is the name */
+/* of the symbol, in the same case form as when created. */
+/* slines[n] is the source line on which the symbol value was first */
+/* assigned */
+/* symbol_debug_backpatch_positions[n] */
+/* is a file position in the debug information file where */
+/* the symbol's value should be written after backpatching, */
+/* or else the null position if the value was known and */
+/* written beforehand */
+/* replacement_debug_backpatch_positions[n] */
+/* is a file position in the debug information file where */
+/* the symbol's name can be erased if it is replaced, or */
+/* else null if the name will never need to be replaced */
+/* */
+/* Comparison is case insensitive. */
+/* Note that local variable names are not entered into the symbols table, */
+/* as their numbers and scope are too limited for this to be efficient. */
+/* ------------------------------------------------------------------------- */
+/* Caveat editor: some array types are set up to work even on machines */
+/* where sizeof(int32 *) differs from, e.g., sizeof(char *): so do not */
+/* alter the types unless you understand what is going on! */
+/* ------------------------------------------------------------------------- */
+
+ int32 **symbs;
+ int32 *svals;
+ int *smarks; /* Glulx-only */
+ brief_location *slines;
+ int *sflags;
+#ifdef VAX
+ char *stypes; /* In VAX C, insanely, "signed char" is illegal */
+#else
+ signed char *stypes;
+#endif
+ maybe_file_position *symbol_debug_backpatch_positions;
+ maybe_file_position *replacement_debug_backpatch_positions;
+
+/* ------------------------------------------------------------------------- */
+/* Memory to hold the text of symbol names: note that this memory is */
+/* allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE. */
+/* ------------------------------------------------------------------------- */
+
+#define MAX_SYMBOL_CHUNKS (100)
+
+static uchar *symbols_free_space, /* Next byte free to hold new names */
+ *symbols_ceiling; /* Pointer to the end of the current
+ allocation of memory for names */
+
+static char** symbol_name_space_chunks; /* For chunks of memory used to hold
+ the name strings of symbols */
+static int no_symbol_name_space_chunks;
+
+typedef struct value_pair_struct {
+ int original_symbol;
+ int renamed_symbol;
+} value_pair_t;
+static value_pair_t *symbol_replacements;
+static int symbol_replacements_count;
+static int symbol_replacements_size; /* calloced size */
+
+/* ------------------------------------------------------------------------- */
+/* The symbols table is "hash-coded" into a disjoint union of linked */
+/* lists, so that for any symbol i, next_entry[i] is either -1 (meaning */
+/* that it's the last in its list) or the next in the list. */
+/* */
+/* Each list contains, in alphabetical order, all the symbols which share */
+/* the same "hash code" (a numerical function of the text of the symbol */
+/* name, designed with the aim that roughly equal numbers of symbols are */
+/* given each possible hash code). The hash codes are 0 to HASH_TAB_SIZE */
+/* (which is a memory setting) minus 1: start_of_list[h] gives the first */
+/* symbol with hash code h, or -1 if no symbol exists with hash code h. */
+/* */
+/* Note that the running time of the symbol search algorithm is about */
+/* */
+/* O ( n^2 / HASH_TAB_SIZE ) */
+/* */
+/* (where n is the number of symbols in the program) so that it is a good */
+/* idea to choose HASH_TAB_SIZE as large as conveniently possible. */
+/* ------------------------------------------------------------------------- */
+
+static int *next_entry;
+static int32 *start_of_list;
+
+/* ------------------------------------------------------------------------- */
+/* Initialisation. */
+/* ------------------------------------------------------------------------- */
+
+static void init_symbol_banks(void)
+{ int i;
+ for (i=0; i<HASH_TAB_SIZE; i++) start_of_list[i] = -1;
+}
+
+/* ------------------------------------------------------------------------- */
+/* The hash coding we use is quite standard; the variable hashcode is */
+/* expected to overflow a good deal. (The aim is to produce a number */
+/* so that similar names do not produce the same number.) Note that */
+/* 30011 is prime. It doesn't matter if the unsigned int to int cast */
+/* behaves differently on different ports. */
+/* ------------------------------------------------------------------------- */
+
+int case_conversion_grid[128];
+
+static void make_case_conversion_grid(void)
+{
+ /* Assumes that A to Z are contiguous in the host OS character set:
+ true for ASCII but not for EBCDIC, for instance. */
+
+ int i;
+ for (i=0; i<128; i++) case_conversion_grid[i] = i;
+ for (i=0; i<26; i++) case_conversion_grid['A'+i]='a'+i;
+}
+
+extern int hash_code_from_string(char *p)
+{ uint32 hashcode=0;
+ for (; *p; p++) hashcode=hashcode*30011 + case_conversion_grid[(uchar)*p];
+ return (int) (hashcode % HASH_TAB_SIZE);
+}
+
+extern int strcmpcis(char *p, char *q)
+{
+ /* Case insensitive strcmp */
+
+ int i, j, pc, qc;
+ for (i=0;p[i] != 0;i++)
+ { pc = p[i]; if (isupper(pc)) pc = tolower(pc);
+ qc = q[i]; if (isupper(qc)) qc = tolower(qc);
+ j = pc - qc;
+ if (j!=0) return j;
+ }
+ qc = q[i]; if (isupper(qc)) qc = tolower(qc);
+ return -qc;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Symbol finding, creating, and removing. */
+/* ------------------------------------------------------------------------- */
+
+extern int symbol_index(char *p, int hashcode)
+{
+ /* Return the index in the symbs/svals/sflags/stypes/... arrays of symbol
+ "p", creating a new symbol with that name if it isn't already there.
+
+ New symbols are created with flag UNKNOWN_SFLAG, value 0x100
+ (a 2-byte quantity in Z-machine terms) and type CONSTANT_T.
+
+ The string "p" is undamaged. */
+
+ int32 new_entry, this, last; char *r;
+
+ if (hashcode == -1) hashcode = hash_code_from_string(p);
+
+ this = start_of_list[hashcode]; last = -1;
+
+ do
+ { if (this == -1) break;
+
+ r = (char *)symbs[this];
+ new_entry = strcmpcis(r, p);
+ if (new_entry == 0)
+ {
+ if (track_unused_routines)
+ df_note_function_symbol(this);
+ return this;
+ }
+ if (new_entry > 0) break;
+
+ last = this;
+ this = next_entry[this];
+ } while (this != -1);
+
+ if (no_symbols >= MAX_SYMBOLS)
+ memoryerror("MAX_SYMBOLS", MAX_SYMBOLS);
+
+ if (last == -1)
+ { next_entry[no_symbols]=start_of_list[hashcode];
+ start_of_list[hashcode]=no_symbols;
+ }
+ else
+ { next_entry[no_symbols]=this;
+ next_entry[last]=no_symbols;
+ }
+
+ if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+ { symbols_free_space
+ = my_malloc(SYMBOLS_CHUNK_SIZE, "symbol names chunk");
+ symbols_ceiling = symbols_free_space + SYMBOLS_CHUNK_SIZE;
+ /* If we've passed MAX_SYMBOL_CHUNKS chunks, we print an error
+ message telling the user to increase SYMBOLS_CHUNK_SIZE.
+ That is the correct cure, even though the error comes out
+ worded inaccurately. */
+ if (no_symbol_name_space_chunks >= MAX_SYMBOL_CHUNKS)
+ memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+ symbol_name_space_chunks[no_symbol_name_space_chunks++]
+ = (char *) symbols_free_space;
+ if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+ memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+ }
+
+ strcpy((char *) symbols_free_space, p);
+ symbs[no_symbols] = (int32 *) symbols_free_space;
+ symbols_free_space += strlen((char *)symbols_free_space) + 1;
+
+ svals[no_symbols] = 0x100; /* ###-wrong? Would this fix the
+ unbound-symbol-causes-asm-error? */
+ sflags[no_symbols] = UNKNOWN_SFLAG;
+ stypes[no_symbols] = CONSTANT_T;
+ slines[no_symbols] = get_brief_location(&ErrorReport);
+ if (debugfile_switch)
+ { nullify_debug_file_position
+ (&symbol_debug_backpatch_positions[no_symbols]);
+ nullify_debug_file_position
+ (&replacement_debug_backpatch_positions[no_symbols]);
+ }
+
+ if (track_unused_routines)
+ df_note_function_symbol(no_symbols);
+ return(no_symbols++);
+}
+
+extern void end_symbol_scope(int k)
+{
+ /* Remove the given symbol from the hash table, making it
+ invisible to symbol_index. This is used by the Undef directive.
+ If the symbol is not found, this silently does nothing.
+ */
+
+ int j;
+ j = hash_code_from_string((char *) symbs[k]);
+ if (start_of_list[j] == k)
+ { start_of_list[j] = next_entry[k];
+ return;
+ }
+ j = start_of_list[j];
+ while (j != -1)
+ {
+ if (next_entry[j] == k)
+ { next_entry[j] = next_entry[k];
+ return;
+ }
+ j = next_entry[j];
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Printing diagnostics */
+/* ------------------------------------------------------------------------- */
+
+extern char *typename(int type)
+{ switch(type)
+ {
+ /* These are the possible symbol types. Note that local variables
+ do not reside in the symbol table (for scope and efficiency
+ reasons) and actions have their own name-space (via routine
+ names with "Sub" appended). */
+
+ case ROUTINE_T: return("Routine");
+ case LABEL_T: return("Label");
+ case GLOBAL_VARIABLE_T: return("Global variable");
+ case ARRAY_T: return("Array");
+ case CONSTANT_T: return("Defined constant");
+ case ATTRIBUTE_T: return("Attribute");
+ case PROPERTY_T: return("Property");
+ case INDIVIDUAL_PROPERTY_T: return("Individual property");
+ case OBJECT_T: return("Object");
+ case CLASS_T: return("Class");
+ case FAKE_ACTION_T: return("Fake action");
+
+ default: return("(Unknown type)");
+ }
+}
+
+static void describe_flags(int flags)
+{ if (flags & UNKNOWN_SFLAG) printf("(?) ");
+ if (flags & USED_SFLAG) printf("(used) ");
+ if (flags & REPLACE_SFLAG) printf("(Replaced) ");
+ if (flags & DEFCON_SFLAG) printf("(Defaulted) ");
+ if (flags & STUB_SFLAG) printf("(Stubbed) ");
+ if (flags & CHANGE_SFLAG) printf("(value will change) ");
+ if (flags & IMPORT_SFLAG) printf("(Imported) ");
+ if (flags & EXPORT_SFLAG) printf("(Exported) ");
+ if (flags & SYSTEM_SFLAG) printf("(System) ");
+ if (flags & INSF_SFLAG) printf("(created in sys file) ");
+ if (flags & UERROR_SFLAG) printf("('Unknown' error issued) ");
+ if (flags & ALIASED_SFLAG) printf("(aliased) ");
+ if (flags & ACTION_SFLAG) printf("(Action name) ");
+ if (flags & REDEFINABLE_SFLAG) printf("(Redefinable) ");
+}
+
+extern void describe_symbol(int k)
+{ printf("%4d %-16s %2d:%04d %04x %s ",
+ k, (char *) (symbs[k]),
+ (int)(slines[k].file_index),
+ (int)(slines[k].line_number),
+ svals[k], typename(stypes[k]));
+ describe_flags(sflags[k]);
+}
+
+extern void list_symbols(int level)
+{ int k;
+ for (k=0; k<no_symbols; k++)
+ { if ((level==2) ||
+ ((sflags[k] & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
+ { describe_symbol(k); printf("\n");
+ }
+ }
+}
+
+extern void issue_unused_warnings(void)
+{ int32 i;
+
+ if (module_switch) return;
+
+ /* Update any ad-hoc variables that might help the library */
+ if (glulx_mode)
+ { global_initial_value[10]=statusline_flag;
+ }
+ /* Now back to mark anything necessary as used */
+
+ i = symbol_index("Main", -1);
+ if (!(sflags[i] & UNKNOWN_SFLAG)) sflags[i] |= USED_SFLAG;
+
+ for (i=0;i<no_symbols;i++)
+ { if (((sflags[i]
+ & (SYSTEM_SFLAG + UNKNOWN_SFLAG + EXPORT_SFLAG
+ + INSF_SFLAG + USED_SFLAG + REPLACE_SFLAG)) == 0)
+ && (stypes[i] != OBJECT_T))
+ dbnu_warning(typename(stypes[i]), (char *) symbs[i], slines[i]);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* These are arrays used only during story file (never module) creation, */
+/* and not allocated until then. */
+
+ int32 *individual_name_strings; /* Packed addresses of Z-encoded
+ strings of the names of the
+ properties: this is an array
+ indexed by the property ID */
+ int32 *action_name_strings; /* Ditto for actions */
+ int32 *attribute_name_strings; /* Ditto for attributes */
+ int32 *array_name_strings; /* Ditto for arrays */
+
+extern void write_the_identifier_names(void)
+{ int i, j, k, t, null_value; char idname_string[256];
+ static char unknown_attribute[20] = "<unknown attribute>";
+
+ for (i=0; i<no_individual_properties; i++)
+ individual_name_strings[i] = 0;
+
+ if (module_switch) return;
+
+ veneer_mode = TRUE;
+
+ null_value = compile_string(unknown_attribute, FALSE, FALSE);
+ for (i=0; i<NUM_ATTR_BYTES*8; i++) attribute_name_strings[i] = null_value;
+
+ for (i=0; i<no_symbols; i++)
+ { t=stypes[i];
+ if ((t == INDIVIDUAL_PROPERTY_T) || (t == PROPERTY_T))
+ { if (sflags[i] & ALIASED_SFLAG)
+ { if (individual_name_strings[svals[i]] == 0)
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+
+ for (j=i+1, k=0; (j<no_symbols && k<3); j++)
+ { if ((stypes[j] == stypes[i])
+ && (svals[j] == svals[i]))
+ { sprintf(idname_string+strlen(idname_string),
+ "/%s", (char *) symbs[j]);
+ k++;
+ }
+ }
+
+ individual_name_strings[svals[i]]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+ else
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+
+ individual_name_strings[svals[i]]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+ if (t == ATTRIBUTE_T)
+ { if (sflags[i] & ALIASED_SFLAG)
+ { if (attribute_name_strings[svals[i]] == null_value)
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+
+ for (j=i+1, k=0; (j<no_symbols && k<3); j++)
+ { if ((stypes[j] == stypes[i])
+ && (svals[j] == svals[i]))
+ { sprintf(idname_string+strlen(idname_string),
+ "/%s", (char *) symbs[j]);
+ k++;
+ }
+ }
+
+ attribute_name_strings[svals[i]]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+ else
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+
+ attribute_name_strings[svals[i]]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+ if (sflags[i] & ACTION_SFLAG)
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+ idname_string[strlen(idname_string)-3] = 0;
+
+ if (debugfile_switch)
+ { debug_file_printf("<action>");
+ debug_file_printf
+ ("<identifier>##%s</identifier>", idname_string);
+ debug_file_printf("<value>%d</value>", svals[i]);
+ debug_file_printf("</action>");
+ }
+
+ action_name_strings[svals[i]]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+
+ for (i=0; i<no_symbols; i++)
+ { if (stypes[i] == FAKE_ACTION_T)
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+ idname_string[strlen(idname_string)-3] = 0;
+
+ action_name_strings[svals[i]
+ - ((grammar_version_number==1)?256:4096) + no_actions]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+
+ for (j=0; j<no_arrays; j++)
+ { i = array_symbols[j];
+ sprintf(idname_string, "%s", (char *) symbs[i]);
+
+ array_name_strings[j]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ if (define_INFIX_switch)
+ { for (i=0; i<no_symbols; i++)
+ { if (stypes[i] == GLOBAL_VARIABLE_T)
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+ array_name_strings[no_arrays + svals[i] -16]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+
+ for (i=0; i<no_named_routines; i++)
+ { sprintf(idname_string, "%s", (char *) symbs[named_routine_symbols[i]]);
+ array_name_strings[no_arrays + no_globals + i]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+
+ for (i=0, no_named_constants=0; i<no_symbols; i++)
+ { if (((stypes[i] == OBJECT_T) || (stypes[i] == CLASS_T)
+ || (stypes[i] == CONSTANT_T))
+ && ((sflags[i] & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
+ { sprintf(idname_string, "%s", (char *) symbs[i]);
+ array_name_strings[no_arrays + no_globals + no_named_routines
+ + no_named_constants++]
+ = compile_string(idname_string, FALSE, FALSE);
+ }
+ }
+ }
+
+ veneer_mode = FALSE;
+}
+/* ------------------------------------------------------------------------- */
+/* Creating symbols */
+/* ------------------------------------------------------------------------- */
+
+static void assign_symbol_base(int index, int32 value, int type)
+{ svals[index] = value;
+ stypes[index] = type;
+ if (sflags[index] & UNKNOWN_SFLAG)
+ { sflags[index] &= (~UNKNOWN_SFLAG);
+ if (is_systemfile()) sflags[index] |= INSF_SFLAG;
+ slines[index] = get_brief_location(&ErrorReport);
+ }
+}
+
+extern void assign_symbol(int index, int32 value, int type)
+{
+ if (!glulx_mode) {
+ assign_symbol_base(index, value, type);
+ }
+ else {
+ smarks[index] = 0;
+ assign_symbol_base(index, value, type);
+ }
+}
+
+extern void assign_marked_symbol(int index, int marker, int32 value, int type)
+{
+ if (!glulx_mode) {
+ assign_symbol_base(index, (int32)marker*0x10000 + (value % 0x10000),
+ type);
+ }
+ else {
+ smarks[index] = marker;
+ assign_symbol_base(index, value, type);
+ }
+}
+
+static void emit_debug_information_for_predefined_symbol
+ (char *name, int32 symbol, int32 value, int type)
+{ if (debugfile_switch)
+ { switch (type)
+ { case CONSTANT_T:
+ debug_file_printf("<constant>");
+ debug_file_printf("<identifier>%s</identifier>", name);
+ write_debug_symbol_optional_backpatch(symbol);
+ debug_file_printf("</constant>");
+ break;
+ case GLOBAL_VARIABLE_T:
+ debug_file_printf("<global-variable>");
+ debug_file_printf("<identifier>%s</identifier>", name);
+ debug_file_printf("<address>");
+ write_debug_global_backpatch(value);
+ debug_file_printf("</address>");
+ debug_file_printf("</global-variable>");
+ break;
+ case OBJECT_T:
+ if (value)
+ { compiler_error("Non-nothing object predefined");
+ }
+ debug_file_printf("<object>");
+ debug_file_printf("<identifier>%s</identifier>", name);
+ debug_file_printf("<value>0</value>");
+ debug_file_printf("</object>");
+ break;
+ case ATTRIBUTE_T:
+ debug_file_printf("<attribute>");
+ debug_file_printf("<identifier>%s</identifier>", name);
+ debug_file_printf("<value>%d</value>", value);
+ debug_file_printf("</attribute>");
+ break;
+ case PROPERTY_T:
+ case INDIVIDUAL_PROPERTY_T:
+ debug_file_printf("<property>");
+ debug_file_printf("<identifier>%s</identifier>", name);
+ debug_file_printf("<value>%d</value>", value);
+ debug_file_printf("</property>");
+ break;
+ default:
+ compiler_error
+ ("Unable to emit debug information for predefined symbol");
+ break;
+ }
+ }
+}
+
+static void create_symbol(char *p, int32 value, int type)
+{ int i = symbol_index(p, -1);
+ svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+ sflags[i] = USED_SFLAG + SYSTEM_SFLAG;
+ emit_debug_information_for_predefined_symbol(p, i, value, type);
+}
+
+static void create_rsymbol(char *p, int value, int type)
+{ int i = symbol_index(p, -1);
+ svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+ sflags[i] = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
+ emit_debug_information_for_predefined_symbol(p, i, value, type);
+}
+
+static void stockup_symbols(void)
+{
+ if (!glulx_mode)
+ create_symbol("TARGET_ZCODE", 0, CONSTANT_T);
+ else
+ create_symbol("TARGET_GLULX", 0, CONSTANT_T);
+
+ create_symbol("nothing", 0, OBJECT_T);
+ create_symbol("name", 1, PROPERTY_T);
+
+ create_symbol("true", 1, CONSTANT_T);
+ create_symbol("false", 0, CONSTANT_T);
+
+ /* Glulx defaults to GV2; Z-code to GV1 */
+ if (!glulx_mode)
+ create_rsymbol("Grammar__Version", 1, CONSTANT_T);
+ else
+ create_rsymbol("Grammar__Version", 2, CONSTANT_T);
+ grammar_version_symbol = symbol_index("Grammar__Version", -1);
+
+ if (module_switch)
+ create_rsymbol("MODULE_MODE",0, CONSTANT_T);
+
+ if (runtime_error_checking_switch)
+ create_rsymbol("STRICT_MODE",0, CONSTANT_T);
+
+ if (define_DEBUG_switch)
+ create_rsymbol("DEBUG", 0, CONSTANT_T);
+
+ if (define_USE_MODULES_switch)
+ create_rsymbol("USE_MODULES",0, CONSTANT_T);
+
+ if (define_INFIX_switch)
+ { create_rsymbol("INFIX", 0, CONSTANT_T);
+ create_symbol("infix__watching", 0, ATTRIBUTE_T);
+ }
+
+ create_symbol("WORDSIZE", WORDSIZE, CONSTANT_T);
+ create_symbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
+ if (!glulx_mode) {
+ create_symbol("DICT_WORD_SIZE", ((version_number==3)?4:6), CONSTANT_T);
+ create_symbol("NUM_ATTR_BYTES", ((version_number==3)?4:6), CONSTANT_T);
+ }
+ else {
+ create_symbol("DICT_WORD_SIZE", DICT_WORD_SIZE, CONSTANT_T);
+ create_symbol("DICT_CHAR_SIZE", DICT_CHAR_SIZE, CONSTANT_T);
+ if (DICT_CHAR_SIZE != 1)
+ create_symbol("DICT_IS_UNICODE", 1, CONSTANT_T);
+ create_symbol("NUM_ATTR_BYTES", NUM_ATTR_BYTES, CONSTANT_T);
+ create_symbol("GOBJFIELD_CHAIN", GOBJFIELD_CHAIN(), CONSTANT_T);
+ create_symbol("GOBJFIELD_NAME", GOBJFIELD_NAME(), CONSTANT_T);
+ create_symbol("GOBJFIELD_PROPTAB", GOBJFIELD_PROPTAB(), CONSTANT_T);
+ create_symbol("GOBJFIELD_PARENT", GOBJFIELD_PARENT(), CONSTANT_T);
+ create_symbol("GOBJFIELD_SIBLING", GOBJFIELD_SIBLING(), CONSTANT_T);
+ create_symbol("GOBJFIELD_CHILD", GOBJFIELD_CHILD(), CONSTANT_T);
+ create_symbol("GOBJ_EXT_START", 1+NUM_ATTR_BYTES+6*WORDSIZE, CONSTANT_T);
+ create_symbol("GOBJ_TOTAL_LENGTH", 1+NUM_ATTR_BYTES+6*WORDSIZE+GLULX_OBJECT_EXT_BYTES, CONSTANT_T);
+ create_symbol("INDIV_PROP_START", INDIV_PROP_START, CONSTANT_T);
+ }
+
+ if (!glulx_mode) {
+ create_symbol("temp_global", 255, GLOBAL_VARIABLE_T);
+ create_symbol("temp__global2", 254, GLOBAL_VARIABLE_T);
+ create_symbol("temp__global3", 253, GLOBAL_VARIABLE_T);
+ create_symbol("temp__global4", 252, GLOBAL_VARIABLE_T);
+ create_symbol("self", 251, GLOBAL_VARIABLE_T);
+ create_symbol("sender", 250, GLOBAL_VARIABLE_T);
+ create_symbol("sw__var", 249, GLOBAL_VARIABLE_T);
+
+ create_symbol("sys__glob0", 16, GLOBAL_VARIABLE_T);
+ create_symbol("sys__glob1", 17, GLOBAL_VARIABLE_T);
+ create_symbol("sys__glob2", 18, GLOBAL_VARIABLE_T);
+
+ create_symbol("create", 64, INDIVIDUAL_PROPERTY_T);
+ create_symbol("recreate", 65, INDIVIDUAL_PROPERTY_T);
+ create_symbol("destroy", 66, INDIVIDUAL_PROPERTY_T);
+ create_symbol("remaining", 67, INDIVIDUAL_PROPERTY_T);
+ create_symbol("copy", 68, INDIVIDUAL_PROPERTY_T);
+ create_symbol("call", 69, INDIVIDUAL_PROPERTY_T);
+ create_symbol("print", 70, INDIVIDUAL_PROPERTY_T);
+ create_symbol("print_to_array",71, INDIVIDUAL_PROPERTY_T);
+ }
+ else {
+ /* In Glulx, these system globals are entered in order, not down
+ from 255. */
+ create_symbol("temp_global", MAX_LOCAL_VARIABLES+0,
+ GLOBAL_VARIABLE_T);
+ create_symbol("temp__global2", MAX_LOCAL_VARIABLES+1,
+ GLOBAL_VARIABLE_T);
+ create_symbol("temp__global3", MAX_LOCAL_VARIABLES+2,
+ GLOBAL_VARIABLE_T);
+ create_symbol("temp__global4", MAX_LOCAL_VARIABLES+3,
+ GLOBAL_VARIABLE_T);
+ create_symbol("self", MAX_LOCAL_VARIABLES+4,
+ GLOBAL_VARIABLE_T);
+ create_symbol("sender", MAX_LOCAL_VARIABLES+5,
+ GLOBAL_VARIABLE_T);
+ create_symbol("sw__var", MAX_LOCAL_VARIABLES+6,
+ GLOBAL_VARIABLE_T);
+
+ /* These are almost certainly meaningless, and can be removed. */
+ create_symbol("sys__glob0", MAX_LOCAL_VARIABLES+7,
+ GLOBAL_VARIABLE_T);
+ create_symbol("sys__glob1", MAX_LOCAL_VARIABLES+8,
+ GLOBAL_VARIABLE_T);
+ create_symbol("sys__glob2", MAX_LOCAL_VARIABLES+9,
+ GLOBAL_VARIABLE_T);
+
+ /* value of statusline_flag to be written later */
+ create_symbol("sys_statusline_flag", MAX_LOCAL_VARIABLES+10,
+ GLOBAL_VARIABLE_T);
+
+ /* These are created in order, but not necessarily at a fixed
+ value. */
+ create_symbol("create", INDIV_PROP_START+0,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("recreate", INDIV_PROP_START+1,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("destroy", INDIV_PROP_START+2,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("remaining", INDIV_PROP_START+3,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("copy", INDIV_PROP_START+4,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("call", INDIV_PROP_START+5,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("print", INDIV_PROP_START+6,
+ INDIVIDUAL_PROPERTY_T);
+ create_symbol("print_to_array",INDIV_PROP_START+7,
+ INDIVIDUAL_PROPERTY_T);
+
+ /* Floating-point constants. Note that FLOAT_NINFINITY is not
+ -FLOAT_INFINITY, because float negation doesn't work that
+ way. Also note that FLOAT_NAN is just one of many possible
+ "not-a-number" values. */
+ create_symbol("FLOAT_INFINITY", 0x7F800000, CONSTANT_T);
+ create_symbol("FLOAT_NINFINITY", 0xFF800000, CONSTANT_T);
+ create_symbol("FLOAT_NAN", 0x7FC00000, CONSTANT_T);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* The symbol replacement table. This is needed only for the */
+/* "Replace X Y" directive. */
+/* ------------------------------------------------------------------------- */
+
+extern void add_symbol_replacement_mapping(int original, int renamed)
+{
+ int ix;
+
+ if (original == renamed) {
+ error_named("A routine cannot be 'Replace'd to itself:", (char *)symbs[original]);
+ return;
+ }
+
+ if (symbol_replacements_count == symbol_replacements_size) {
+ int oldsize = symbol_replacements_size;
+ if (symbol_replacements_size == 0)
+ symbol_replacements_size = 4;
+ else
+ symbol_replacements_size *= 2;
+ my_recalloc(&symbol_replacements, sizeof(value_pair_t), oldsize,
+ symbol_replacements_size, "symbol replacement table");
+ }
+
+ /* If the original form is already in our table, report an error.
+ Same goes if the replaced form is already in the table as an
+ original. (Other collision cases have already been
+ detected.) */
+
+ for (ix=0; ix<symbol_replacements_count; ix++) {
+ if (original == symbol_replacements[ix].original_symbol) {
+ error_named("A routine cannot be 'Replace'd to more than one new name:", (char *)symbs[original]);
+ }
+ if (renamed == symbol_replacements[ix].original_symbol) {
+ error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", (char *)symbs[original]);
+ }
+ }
+
+ symbol_replacements[symbol_replacements_count].original_symbol = original;
+ symbol_replacements[symbol_replacements_count].renamed_symbol = renamed;
+ symbol_replacements_count++;
+}
+
+extern int find_symbol_replacement(int *value)
+{
+ int changed = FALSE;
+ int ix;
+
+ if (!symbol_replacements)
+ return FALSE;
+
+ for (ix=0; ix<symbol_replacements_count; ix++) {
+ if (*value == symbol_replacements[ix].original_symbol) {
+ *value = symbol_replacements[ix].renamed_symbol;
+ changed = TRUE;
+ }
+ }
+
+ return changed;
+}
+
+/* ------------------------------------------------------------------------- */
+/* The dead-function removal optimization. */
+/* ------------------------------------------------------------------------- */
+
+int track_unused_routines; /* set if either WARN_UNUSED_ROUTINES or
+ OMIT_UNUSED_ROUTINES is nonzero */
+int df_dont_note_global_symbols; /* temporarily set at times in parsing */
+static int df_tables_closed; /* set at end of compiler pass */
+
+typedef struct df_function_struct df_function_t;
+typedef struct df_reference_struct df_reference_t;
+
+struct df_function_struct {
+ char *name; /* borrowed reference, generally to the symbs[] table */
+ brief_location source_line; /* copied from routine_starts_line */
+ int sysfile; /* does this occur in a system file? */
+ uint32 address; /* function offset in zcode_area (not the final address) */
+ uint32 newaddress; /* function offset after stripping */
+ uint32 length;
+ int usage;
+ df_reference_t *refs; /* chain of references made *from* this function */
+ int processed;
+
+ df_function_t *funcnext; /* in forward functions order */
+ df_function_t *todonext; /* in the todo chain */
+ df_function_t *next; /* in the hash table */
+};
+
+struct df_reference_struct {
+ uint32 address; /* function offset in zcode_area (not the final address) */
+ int symbol; /* index in symbols array */
+
+ df_reference_t *refsnext; /* in the function's refs chain */
+ df_reference_t *next; /* in the hash table */
+};
+
+/* Bitmask flags for how functions are used: */
+#define DF_USAGE_GLOBAL (1<<0) /* In a global variable, array, etc */
+#define DF_USAGE_EMBEDDED (1<<1) /* An anonymous function in a property */
+#define DF_USAGE_MAIN (1<<2) /* Main() or Main__() */
+#define DF_USAGE_FUNCTION (1<<3) /* Used from another used function */
+
+#define DF_FUNCTION_HASH_BUCKETS (1023)
+
+/* Table of all compiled functions. (Only created if track_unused_routines
+ is set.) This is a hash table. */
+static df_function_t **df_functions;
+/* List of all compiled functions, in address order. The first entry
+ has address DF_NOT_IN_FUNCTION, and stands in for the global namespace. */
+static df_function_t *df_functions_head;
+static df_function_t *df_functions_tail;
+/* Used during output_file(), to track how far the code-area output has
+ gotten. */
+static df_function_t *df_iterator;
+
+/* Array of all compiled functions in address order. (Does not include
+ the global namespace entry.) This is generated only if needed. */
+static df_function_t **df_functions_sorted;
+static int df_functions_sorted_count;
+
+#define DF_NOT_IN_FUNCTION ((uint32)0xFFFFFFFF)
+#define DF_SYMBOL_HASH_BUCKETS (4095)
+
+/* Map of what functions reference what other functions. (Only created if
+ track_unused_routines is set.) */
+static df_reference_t **df_symbol_map;
+
+/* Globals used while a function is being compiled. When a function
+ *isn't* being compiled, df_current_function_addr will be DF_NOT_IN_FUNCTION
+ and df_current_function will refer to the global namespace record. */
+static df_function_t *df_current_function;
+static char *df_current_function_name;
+static uint32 df_current_function_addr;
+
+/* Size totals for compiled code. These are only meaningful if
+ track_unused_routines is true. (If we're only doing WARN_UNUSED_ROUTINES,
+ these values will be set, but the "after" value will not affect the
+ final game file.) */
+uint32 df_total_size_before_stripping;
+uint32 df_total_size_after_stripping;
+
+/* When we begin compiling a function, call this to note that fact.
+ Any symbol referenced from now on will be associated with the function.
+*/
+extern void df_note_function_start(char *name, uint32 address,
+ int embedded_flag, brief_location source_line)
+{
+ df_function_t *func;
+ int bucket;
+
+ if (df_tables_closed)
+ error("Internal error in stripping: Tried to start a new function after tables were closed.");
+
+ /* We retain the name only for debugging output. Note that embedded
+ functions all show up as "<embedded>" -- their "obj.prop" name
+ never gets stored in permanent memory. */
+ df_current_function_name = name;
+ df_current_function_addr = address;
+
+ func = my_malloc(sizeof(df_function_t), "df function entry");
+ memset(func, 0, sizeof(df_function_t));
+ func->name = name;
+ func->address = address;
+ func->source_line = source_line;
+ func->sysfile = (address == DF_NOT_IN_FUNCTION || is_systemfile());
+ /* An embedded function is stored in an object property, so we
+ consider it to be used a priori. */
+ if (embedded_flag)
+ func->usage |= DF_USAGE_EMBEDDED;
+
+ if (!df_functions_head) {
+ df_functions_head = func;
+ df_functions_tail = func;
+ }
+ else {
+ df_functions_tail->funcnext = func;
+ df_functions_tail = func;
+ }
+
+ bucket = address % DF_FUNCTION_HASH_BUCKETS;
+ func->next = df_functions[bucket];
+ df_functions[bucket] = func;
+
+ df_current_function = func;
+}
+
+/* When we're done compiling a function, call this. Any symbol referenced
+ from now on will be associated with the global namespace.
+*/
+extern void df_note_function_end(uint32 endaddress)
+{
+ df_current_function->length = endaddress - df_current_function->address;
+
+ df_current_function_name = NULL;
+ df_current_function_addr = DF_NOT_IN_FUNCTION;
+ df_current_function = df_functions_head; /* the global namespace */
+}
+
+/* Find the function record for a given address. (Addresses are offsets
+ in zcode_area.)
+*/
+static df_function_t *df_function_for_address(uint32 address)
+{
+ int bucket = address % DF_FUNCTION_HASH_BUCKETS;
+ df_function_t *func;
+ for (func = df_functions[bucket]; func; func = func->next) {
+ if (func->address == address)
+ return func;
+ }
+ return NULL;
+}
+
+/* Whenever a function is referenced, we call this to note who called it.
+*/
+extern void df_note_function_symbol(int symbol)
+{
+ int bucket, symtype;
+ df_reference_t *ent;
+
+ /* If the compiler pass is over, looking up symbols does not create
+ a global reference. */
+ if (df_tables_closed)
+ return;
+ /* In certain cases during parsing, looking up symbols does not
+ create a global reference. (For example, when reading the name
+ of a function being defined.) */
+ if (df_dont_note_global_symbols)
+ return;
+
+ /* We are only interested in functions, or forward-declared symbols
+ that might turn out to be functions. */
+ symtype = stypes[symbol];
+ if (symtype != ROUTINE_T && symtype != CONSTANT_T)
+ return;
+ if (symtype == CONSTANT_T && !(sflags[symbol] & UNKNOWN_SFLAG))
+ return;
+
+ bucket = (df_current_function_addr ^ (uint32)symbol) % DF_SYMBOL_HASH_BUCKETS;
+ for (ent = df_symbol_map[bucket]; ent; ent = ent->next) {
+ if (ent->address == df_current_function_addr && ent->symbol == symbol)
+ return;
+ }
+
+ /* Create a new reference entry in df_symbol_map. */
+ ent = my_malloc(sizeof(df_reference_t), "df symbol map entry");
+ ent->address = df_current_function_addr;
+ ent->symbol = symbol;
+ ent->next = df_symbol_map[bucket];
+ df_symbol_map[bucket] = ent;
+
+ /* Add the reference to the function's entry as well. */
+ /* The current function is the most recently added, so it will be
+ at the top of its bucket. That makes this call fast. Unless
+ we're in global scope, in which case it might be slower.
+ (I suppose we could cache the df_function_t pointer of the
+ current function, to speed things up.) */
+ if (!df_current_function || df_current_function_addr != df_current_function->address)
+ compiler_error("DF: df_current_function does not match current address.");
+ ent->refsnext = df_current_function->refs;
+ df_current_function->refs = ent;
+}
+
+/* This does the hard work of figuring out what functions are truly dead.
+ It's called near the end of run_pass() in inform.c.
+*/
+extern void locate_dead_functions(void)
+{
+ df_function_t *func, *tofunc;
+ df_reference_t *ent;
+ int ix;
+
+ if (!track_unused_routines)
+ compiler_error("DF: locate_dead_functions called, but function references have not been mapped");
+
+ df_tables_closed = TRUE;
+ df_current_function = NULL;
+
+ /* Note that Main__ was tagged as global implicitly during
+ compile_initial_routine(). Main was tagged during
+ issue_unused_warnings(). But for the sake of thoroughness,
+ we'll mark them specially. */
+
+ ix = symbol_index("Main__", -1);
+ if (stypes[ix] == ROUTINE_T) {
+ uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+ tofunc = df_function_for_address(addr);
+ if (tofunc)
+ tofunc->usage |= DF_USAGE_MAIN;
+ }
+ ix = symbol_index("Main", -1);
+ if (stypes[ix] == ROUTINE_T) {
+ uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+ tofunc = df_function_for_address(addr);
+ if (tofunc)
+ tofunc->usage |= DF_USAGE_MAIN;
+ }
+
+ /* Go through all the functions referenced at the global level;
+ mark them as used. */
+
+ func = df_functions_head;
+ if (!func || func->address != DF_NOT_IN_FUNCTION)
+ compiler_error("DF: Global namespace entry is not at the head of the chain.");
+
+ for (ent = func->refs; ent; ent=ent->refsnext) {
+ uint32 addr;
+ int symbol = ent->symbol;
+ if (stypes[symbol] != ROUTINE_T)
+ continue;
+ addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+ tofunc = df_function_for_address(addr);
+ if (!tofunc) {
+ error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+ continue;
+ }
+ /* A function may be marked here more than once. That's fine. */
+ tofunc->usage |= DF_USAGE_GLOBAL;
+ }
+
+ /* Perform a breadth-first search through functions, starting with
+ the ones that are known to be used at the top level. */
+ {
+ df_function_t *todo, *todotail;
+ df_function_t *func;
+ todo = NULL;
+ todotail = NULL;
+
+ for (func = df_functions_head; func; func = func->funcnext) {
+ if (func->address == DF_NOT_IN_FUNCTION)
+ continue;
+ if (func->usage == 0)
+ continue;
+ if (!todo) {
+ todo = func;
+ todotail = func;
+ }
+ else {
+ todotail->todonext = func;
+ todotail = func;
+ }
+ }
+
+ /* todo is a linked list of functions which are known to be
+ used. If a function's usage field is nonzero, it must be
+ either be on the todo list or have come off already (in
+ which case processed will be set). */
+
+ while (todo) {
+ /* Pop the next function. */
+ func = todo;
+ todo = todo->todonext;
+ if (!todo)
+ todotail = NULL;
+
+ if (func->processed)
+ error_named("Internal error in stripping: function has been processed twice:", func->name);
+
+ /* Go through the function's symbol references. Any
+ reference to a routine, push it into the todo list (if
+ it isn't there already). */
+
+ for (ent = func->refs; ent; ent=ent->refsnext) {
+ uint32 addr;
+ int symbol = ent->symbol;
+ if (stypes[symbol] != ROUTINE_T)
+ continue;
+ addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+ tofunc = df_function_for_address(addr);
+ if (!tofunc) {
+ error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+ continue;
+ }
+ if (tofunc->usage)
+ continue;
+
+ /* Not yet known to be used. Add it to the todo list. */
+ tofunc->usage |= DF_USAGE_FUNCTION;
+ if (!todo) {
+ todo = tofunc;
+ todotail = tofunc;
+ }
+ else {
+ todotail->todonext = tofunc;
+ todotail = tofunc;
+ }
+ }
+
+ func->processed = TRUE;
+ }
+ }
+
+ /* Go through all functions; figure out how much space is consumed,
+ with and without useless functions. */
+
+ {
+ df_function_t *func;
+
+ df_total_size_before_stripping = 0;
+ df_total_size_after_stripping = 0;
+
+ for (func = df_functions_head; func; func = func->funcnext) {
+ if (func->address == DF_NOT_IN_FUNCTION)
+ continue;
+
+ if (func->address != df_total_size_before_stripping)
+ compiler_error("DF: Address gap in function list");
+
+ df_total_size_before_stripping += func->length;
+ if (func->usage) {
+ func->newaddress = df_total_size_after_stripping;
+ df_total_size_after_stripping += func->length;
+ }
+
+ if (!glulx_mode && (df_total_size_after_stripping % scale_factor != 0))
+ compiler_error("DF: New function address is not aligned");
+
+ if (WARN_UNUSED_ROUTINES && !func->usage) {
+ if (!func->sysfile || WARN_UNUSED_ROUTINES >= 2)
+ uncalled_routine_warning("Routine", func->name, func->source_line);
+ }
+ }
+ }
+
+ /* df_measure_hash_table_usage(); */
+}
+
+/* Given an original function address, return where it winds up after
+ unused-function stripping. The function must not itself be unused.
+
+ Both the input and output are offsets, and already scaled by
+ scale_factor.
+
+ This is used by the backpatching system.
+*/
+extern uint32 df_stripped_address_for_address(uint32 addr)
+{
+ df_function_t *func;
+
+ if (!track_unused_routines)
+ compiler_error("DF: df_stripped_address_for_address called, but function references have not been mapped");
+
+ if (!glulx_mode)
+ func = df_function_for_address(addr*scale_factor);
+ else
+ func = df_function_for_address(addr);
+
+ if (!func) {
+ compiler_error("DF: Unable to find function while backpatching");
+ return 0;
+ }
+ if (!func->usage)
+ compiler_error("DF: Tried to backpatch a function address which should be stripped");
+
+ if (!glulx_mode)
+ return func->newaddress / scale_factor;
+ else
+ return func->newaddress;
+}
+
+/* Given an address in the function area, return where it winds up after
+ unused-function stripping. The address can be a function or anywhere
+ within the function. If the address turns out to be in a stripped
+ function, returns 0 (and sets *stripped).
+
+ The input and output are offsets, but *not* scaled.
+
+ This is only used by the debug-file system.
+*/
+uint32 df_stripped_offset_for_code_offset(uint32 offset, int *stripped)
+{
+ df_function_t *func;
+ int count;
+
+ if (!track_unused_routines)
+ compiler_error("DF: df_stripped_offset_for_code_offset called, but function references have not been mapped");
+
+ if (!df_functions_sorted) {
+ /* To do this efficiently, we need a binary-searchable table. Fine,
+ we'll make one. Include both used and unused functions. */
+
+ for (func = df_functions_head, count = 0; func; func = func->funcnext) {
+ if (func->address == DF_NOT_IN_FUNCTION)
+ continue;
+ count++;
+ }
+ df_functions_sorted_count = count;
+
+ df_functions_sorted = my_calloc(sizeof(df_function_t *), df_functions_sorted_count, "df function sorted table");
+
+ for (func = df_functions_head, count = 0; func; func = func->funcnext) {
+ if (func->address == DF_NOT_IN_FUNCTION)
+ continue;
+ df_functions_sorted[count] = func;
+ count++;
+ }
+ }
+
+ /* Do a binary search. Maintain beg <= res < end, where res is the
+ function containing the desired address. */
+ int beg = 0;
+ int end = df_functions_sorted_count;
+
+ /* Set stripped flag until we decide on a non-stripped function. */
+ *stripped = TRUE;
+
+ while (1) {
+ if (beg >= end) {
+ error("DF: offset_for_code_offset: Could not locate address.");
+ return 0;
+ }
+ if (beg+1 == end) {
+ func = df_functions_sorted[beg];
+ if (func->usage == 0)
+ return 0;
+ *stripped = FALSE;
+ return func->newaddress + (offset - func->address);
+ }
+ int new = (beg + end) / 2;
+ if (new <= beg || new >= end)
+ compiler_error("DF: binary search went off the rails");
+
+ func = df_functions_sorted[new];
+ if (offset >= func->address) {
+ if (offset < func->address+func->length) {
+ /* We don't need to loop further; decide here. */
+ if (func->usage == 0)
+ return 0;
+ *stripped = FALSE;
+ return func->newaddress + (offset - func->address);
+ }
+ beg = new;
+ }
+ else {
+ end = new;
+ }
+ }
+}
+
+/* The output_file() routines in files.c have to run down the list of
+ functions, deciding who is in and who is out. But I don't want to
+ export the df_function_t list structure. Instead, I provide this
+ silly iterator pair. Set it up with df_prepare_function_iterate();
+ then repeatedly call df_next_function_iterate().
+*/
+
+extern void df_prepare_function_iterate(void)
+{
+ df_iterator = df_functions_head;
+ if (!df_iterator || df_iterator->address != DF_NOT_IN_FUNCTION)
+ compiler_error("DF: Global namespace entry is not at the head of the chain.");
+ if (!df_iterator->funcnext || df_iterator->funcnext->address != 0)
+ compiler_error("DF: First function entry is not second in the chain.");
+}
+
+/* This returns the end of the next function, and whether the next function
+ is used (live).
+*/
+extern uint32 df_next_function_iterate(int *funcused)
+{
+ if (df_iterator)
+ df_iterator = df_iterator->funcnext;
+ if (!df_iterator) {
+ *funcused = TRUE;
+ return df_total_size_before_stripping+1;
+ }
+ *funcused = (df_iterator->usage != 0);
+ return df_iterator->address + df_iterator->length;
+}
+
+/* ========================================================================= */
+/* Data structure management routines */
+/* ------------------------------------------------------------------------- */
+
+extern void init_symbols_vars(void)
+{
+ symbs = NULL;
+ svals = NULL;
+ smarks = NULL;
+ stypes = NULL;
+ sflags = NULL;
+ next_entry = NULL;
+ start_of_list = NULL;
+
+ symbol_name_space_chunks = NULL;
+ no_symbol_name_space_chunks = 0;
+ symbols_free_space=NULL;
+ symbols_ceiling=symbols_free_space;
+
+ no_symbols = 0;
+
+ symbol_replacements = NULL;
+ symbol_replacements_count = 0;
+ symbol_replacements_size = 0;
+
+ make_case_conversion_grid();
+
+ track_unused_routines = (WARN_UNUSED_ROUTINES || OMIT_UNUSED_ROUTINES);
+ df_tables_closed = FALSE;
+ df_symbol_map = NULL;
+ df_functions = NULL;
+ df_functions_head = NULL;
+ df_functions_tail = NULL;
+ df_current_function = NULL;
+ df_functions_sorted = NULL;
+ df_functions_sorted_count = 0;
+}
+
+extern void symbols_begin_pass(void)
+{
+ df_total_size_before_stripping = 0;
+ df_total_size_after_stripping = 0;
+ df_dont_note_global_symbols = FALSE;
+ df_iterator = NULL;
+}
+
+extern void symbols_allocate_arrays(void)
+{
+ symbs = my_calloc(sizeof(char *), MAX_SYMBOLS, "symbols");
+ svals = my_calloc(sizeof(int32), MAX_SYMBOLS, "symbol values");
+ if (glulx_mode)
+ smarks = my_calloc(sizeof(int), MAX_SYMBOLS, "symbol markers");
+ slines = my_calloc(sizeof(brief_location), MAX_SYMBOLS, "symbol lines");
+ stypes = my_calloc(sizeof(char), MAX_SYMBOLS, "symbol types");
+ sflags = my_calloc(sizeof(int), MAX_SYMBOLS, "symbol flags");
+ if (debugfile_switch)
+ { symbol_debug_backpatch_positions =
+ my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
+ "symbol debug information backpatch positions");
+ replacement_debug_backpatch_positions =
+ my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
+ "replacement debug information backpatch positions");
+ }
+ next_entry = my_calloc(sizeof(int), MAX_SYMBOLS,
+ "symbol linked-list forward links");
+ start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE,
+ "hash code list beginnings");
+
+ symbol_name_space_chunks
+ = my_calloc(sizeof(char *), MAX_SYMBOL_CHUNKS, "symbol names chunk addresses");
+
+ if (track_unused_routines) {
+ df_tables_closed = FALSE;
+
+ df_symbol_map = my_calloc(sizeof(df_reference_t *), DF_SYMBOL_HASH_BUCKETS, "df symbol-map hash table");
+ memset(df_symbol_map, 0, sizeof(df_reference_t *) * DF_SYMBOL_HASH_BUCKETS);
+
+ df_functions = my_calloc(sizeof(df_function_t *), DF_FUNCTION_HASH_BUCKETS, "df function hash table");
+ memset(df_functions, 0, sizeof(df_function_t *) * DF_FUNCTION_HASH_BUCKETS);
+ df_functions_head = NULL;
+ df_functions_tail = NULL;
+
+ df_functions_sorted = NULL;
+ df_functions_sorted_count = 0;
+
+ df_note_function_start("<global namespace>", DF_NOT_IN_FUNCTION, FALSE, blank_brief_location);
+ df_note_function_end(DF_NOT_IN_FUNCTION);
+ /* Now df_current_function is df_functions_head. */
+ }
+
+ init_symbol_banks();
+ stockup_symbols();
+
+ /* Allocated as needed */
+ symbol_replacements = NULL;
+
+ /* Allocated during story file construction, not now */
+ individual_name_strings = NULL;
+ attribute_name_strings = NULL;
+ action_name_strings = NULL;
+ array_name_strings = NULL;
+}
+
+extern void symbols_free_arrays(void)
+{ int i;
+
+ for (i=0; i<no_symbol_name_space_chunks; i++)
+ my_free(&(symbol_name_space_chunks[i]),
+ "symbol names chunk");
+
+ my_free(&symbol_name_space_chunks, "symbol names chunk addresses");
+
+ my_free(&symbs, "symbols");
+ my_free(&svals, "symbol values");
+ my_free(&smarks, "symbol markers");
+ my_free(&slines, "symbol lines");
+ my_free(&stypes, "symbol types");
+ my_free(&sflags, "symbol flags");
+ if (debugfile_switch)
+ { my_free
+ (&symbol_debug_backpatch_positions,
+ "symbol debug information backpatch positions");
+ my_free
+ (&replacement_debug_backpatch_positions,
+ "replacement debug information backpatch positions");
+ }
+ my_free(&next_entry, "symbol linked-list forward links");
+ my_free(&start_of_list, "hash code list beginnings");
+
+ if (symbol_replacements)
+ my_free(&symbol_replacements, "symbol replacement table");
+
+ if (df_symbol_map) {
+ for (i=0; i<DF_SYMBOL_HASH_BUCKETS; i++) {
+ df_reference_t *ent = df_symbol_map[i];
+ while (ent) {
+ df_reference_t *next = ent->next;
+ my_free(&ent, "df symbol map entry");
+ ent = next;
+ }
+ }
+ my_free(&df_symbol_map, "df symbol-map hash table");
+ }
+ if (df_functions_sorted) {
+ my_free(&df_functions, "df function sorted table");
+ }
+ if (df_functions) {
+ for (i=0; i<DF_FUNCTION_HASH_BUCKETS; i++) {
+ df_function_t *func = df_functions[i];
+ while (func) {
+ df_function_t *next = func->next;
+ my_free(&func, "df function entry");
+ func = next;
+ }
+ }
+ my_free(&df_functions, "df function hash table");
+ }
+ df_functions_head = NULL;
+ df_functions_tail = NULL;
+
+ if (individual_name_strings != NULL)
+ my_free(&individual_name_strings, "property name strings");
+ if (action_name_strings != NULL)
+ my_free(&action_name_strings, "action name strings");
+ if (attribute_name_strings != NULL)
+ my_free(&attribute_name_strings, "attribute name strings");
+ if (array_name_strings != NULL)
+ my_free(&array_name_strings, "array name strings");
+}
+
+/* ========================================================================= */