#ifndef EXEC_H__ #define EXEC_H__ 1 /*--------------------------------------------------------------------------- * Types and Macros used to describe and store compiled programs. * *--------------------------------------------------------------------------- * Basics * ------ * * LPMud distinguished between objects and their programs. Objects hold * the live data which is unique to each of them. Programs otoh are shared * among objects through inheritance or cloning and hold the program code, * variable specification and similar information. Due to the separation * programs maintain their own reference count, and it is this separation * which allows the separate swapping of programs and variables. * * A compiled program consists of several data blocks which are allocated * together in one memory block. Not only this is advantageous for the * memory locality, but also simplifies swapping as all data for a program * can be read or written in one go, with a simple relocation computation * on the pointers involved. * * The blocks of a program in correct allocation order are these: * * struct program_s: The basic structure of the program, with pointers to * the other data blocks. * * bytecode program[]: the actual bytecode for the program. * * unsigned short function_names[]: Lookup table of function name index * to the offset of the function within the functions[] table. * function_names[] is allocated together with and right after the * bytecode. If a function redefines an inherited function, this table * points to the redefinition. * Read 'function_name' as 'function_index'. * * uint32 functions[]: One word for each function, giving the type * and the offset within the program[] codearray. Additional information * for each function is embedded into the program code. * If a program uses undefined functions, the compiler generates * dummy functions. * Through inheritance and cross-definition these functions can * be resolved on a higher level. * * string_t *strings[]: An array of pointers to the string literals (stored * as shared strings) used by the program. This way the program can * use the strings simply by an (easily swappable) index. The compiler * makes sure that each string is unique. * * When a program is swapped, the reference counts to these strings are * not removed so that the string literals stay in memory all the time. * This saves time on swap-in, and the string sharing saves more memory * than swapping might gain. * TODO: Does it? * * struct variable_s variables[]: an array describing all variables * with type and name, inherited or own. When a program is swapped, the * reference counts to these strings are not removed so that the * string literals stay in memory all the time. * * struct inherit inherit[]: an array describing all inherited programs. * * vartype_t argument_types[]: (only with #pragma save_types) * The types of all function arguments of the program in the * order they were encountered. * * unsigned short type_start[]: (only with #pragma save_types) * Lookup table [.num_function_names] function index * -> index in .argument_types[], which gives the index of * the first argument type. If this index is INDEX_START_NONE, * the function has no type information. * * include_t includes[]: an array listing all include files used * to compile the program, in the order they were encountered. * When a program is swapped, the reference counts to these strings * are not removed so that the string literals stay in memory all * the time. * * The linenumber information is allocated separately from the main program * block so that it can be swapped separately more easily. The struct * linenumbers_s is allocated to size and holds the line number information * as an array of bytecodes: * * bytecode_t line_numbers[]: the line number information, * encoded in a kind of delta compression. When a program * is swapped in, the line numbers are allocated separately * and not swapped in until needed. * * TODO: If the program_s is allocated separately from the rest, * TODO:: we could swap even if a program is used by clones. * * * Bytecode * -------- * * The bytecode is described and defined in bytecode.h . * * * Inheritance * ----------- * * Inheritance is handled such that all the variable and function * information of the inherited programs are appended to the programs * own variables and functions. The inherit entries then give away * where in the programs tables the inherited information can be found. * * Imagine: A and B inherit nothing, C inherits A, D inherits C and B. * Then the function and variable blocks are set up like this ('-funs' are * real function entries, '-desc' are pointers to inherit descriptors): * * A-fblock: A-funs (B similar) * A-vblock: A-vars (B similar) * * C-fblock: C-funs A-desc * C-vblock: C-vars A-vars * * D-fblock: D-funs (C-desc A-desc) B-desc * D-vblock: D-vars C-vars A-vars B-vars * and fblock has the twist that (C-desc A-desc) together are * considered being 'the' C-desc block. * * If a program is inherited virtually several times, each virtual inherit * gets its own struct inherit; the variable block however is reserved * just once. To mark the virtual inherit, the variable types receive the * modifier TYPE_MOD_VIRTUAL. During the inheritance of the compiler, functions * from non-virtual inherits temporarily receive the flag * NON_VIRTUAL_OFFSET_TAG in their variable indices, too. * * However, accesses to virtually inherited variables only use the first * inherit instance - if the variable index in the child's program code indexes * the variable block associated with a later instance, the driver finds * the first instance by comparing the .prog entries and uses the 'real' * variable associated with this first instance. * * The compiler places virtual variables always at the begin of the variable * block and limits the number to 256. * * TODO: Find a way to avoid the virtual var searching - either by encoding the * TODO:: inherit index in the code, or by adding a ref to the 'first * TODO:: instance' to this structure. See interpret.c:find_virtual_value(). * * Old Comments: * ------------- * When an object is compiled with type testing (#pragma strict_types), all * types are saved of the arguments for that function during compilation. * If the #pragma save_types is specified, then the types are saved even * after compilation, to be used when the object is inherited. *--------------------------------------------------------------------------- */ #include "driver.h" #include "typedefs.h" #include "bytecode.h" typedef unsigned short typeid_t; typedef uint32 typeflags_t; typedef struct vartype_s vartype_t; typedef struct fulltype_s fulltype_t; /* --- Type Constants --- * * These constants and types are used to encode types and visibility * of variables and functions. They are used by both the compiler and * the interpreter. * * Variable/value data types consist of three pieces: * - the primary type constant, optionally modified as * TYPE_MOD_POINTER or TYPE_MOD_REFERENCE; * - the visibility (see below with the function flags for the constants) * - the type object pointer (depending on context either counted or not * counted). * * The pieces show up in these types: * - vartype: datatype + type object pointer * - fulltype: vartype + visibility flags * * TODO: A clean implementation would use just type objects for types. */ /* --- struct vartype_s: Basic type information * * This structure holds the type number, the flags concerning _MOD_POINTER * and _MOD_REFERENCE, and the virtual variable flag (for use in variable_t). * It also holds a pointer to the type object; depending on the context * this reference may be counted or not. */ struct vartype_s { typeid_t type; #ifdef USE_STRUCTS struct_type_t * t_struct; /* For now, only structs have type objects */ #endif }; /* --- struct fulltype_s: Full type information * * This structure holds the type number and all flags: type modifiers and * visibility. * It also holds a pointer to the type object; depending on the context * this reference may be counted or not. * * We do not reuse vartype_t her even though it logically should be, for * two reasons: * - the visibility modifier flags expected that type and flags are * in one single long * - some compilers would add 2 bytes of padding * * TODO: Move the basic visibility into the lower bits so we don't have * TODO:: to use a short for .typeflags. */ struct fulltype_s { typeflags_t typeflags; #ifdef USE_STRUCTS struct_type_t * t_struct; /* For now, only structs have type objects */ #endif }; /* --- Primary type values --- */ #define TYPE_UNKNOWN 0 /* This type must be casted */ #define TYPE_NUMBER 1 #define TYPE_STRING 2 #define TYPE_VOID 3 #define TYPE_OBJECT 4 #define TYPE_MAPPING 5 #define TYPE_FLOAT 6 #define TYPE_ANY 7 /* Will match any type */ #define TYPE_CLOSURE 8 #define TYPE_SYMBOL 9 #define TYPE_QUOTED_ARRAY 10 #ifdef USE_STRUCTS #define TYPE_STRUCT 11 /* Secondary info is the struct id */ #define TYPEMAP_SIZE 12 /* Number of types */ #else #define TYPEMAP_SIZE 11 /* Number of types */ #endif /* USE_STRUCTS */ /* Flags, or'ed on top of the basic type */ #define TYPE_MOD_POINTER 0x0040 /* Pointer to a basic type */ #define TYPE_MOD_REFERENCE 0x0080 /* Reference to a type */ #define TYPE_MOD_MASK 0x000000ff /* Mask for basic type and flags. */ #define PRIMARY_TYPE_MASK (0x0F) /* Mask for the primary type info (sans modifiers) */ #define TYPE_MOD_RMASK (TYPE_MOD_MASK & ~TYPE_MOD_REFERENCE) /* Mask to delete TYPE_MOD_REFERENCE and the visibility mods from * a type value. */ #define TYPEID_MASK (0x0000ffff) /* Mask to mask out just the typeid_t from a typeflags_t. */ #ifdef USE_STRUCTS /* Macros to check a type value for a certain primary type. */ #define IS_TYPE_STRUCT(t) \ (((t).typeflags & (PRIMARY_TYPE_MASK|TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_STRUCT) #define IS_TYPE_ANY(t) \ (((t).typeflags & (PRIMARY_TYPE_MASK|TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_ANY) /* Other type related defines */ #define STRUCT_MAX_MEMBERS 255 /* We allow up to this number of members per struct, so that * we can encode the number of actual members, where needed, * in a single bytecode. */ #endif #define VIRTUAL_VAR_TAG 0x4000 /* Flag set in virtual variables, also interpreted as offset * in the variable index for virtual variables. */ #ifdef USE_STRUCTS /* void ref_vartype_data(vartype_t *v) * Add another reference to the data associated with vartype . * * void ref_fulltype_data(fulltype_t *v) * Add another reference to the data associated with fulltype . */ #define ref_vartype_data(v) \ do{ vartype_t *fvt = v; \ if (fvt->t_struct) (void)ref_struct_type(fvt->t_struct);\ } while(0) #define ref_fulltype_data(v) \ do{ fulltype_t *fvt = v; \ if (fvt->t_struct) (void)ref_struct_type(fvt->t_struct);\ } while(0) /* void free_vartype_data(vartype_t *v) * Free all data associated with vartype . * * void free_fulltype_data(fulltype_t *v) * Free all data associated with fulltype . */ #define free_vartype_data(v) \ do{ vartype_t *fvt = v; \ if (fvt->t_struct) { /* printf("DEBUG: free_vartype(%s) %s %d\n", get_txt(fvt->t_struct->name), __FILE__, __LINE__); */ free_struct_type(fvt->t_struct); }\ fvt->t_struct = NULL;\ } while(0) #define free_fulltype_data(v) \ do{ fulltype_t *fvt = v; \ if (fvt->t_struct) { /* printf("DEBUG: free_fulltype(%s) %s %d\n", get_txt(fvt->t_struct->name), __FILE__, __LINE__); */ free_struct_type(fvt->t_struct); }\ fvt->t_struct = NULL;\ } while(0) #ifdef GC_SUPPORT /* void clear_vartype_ref(vartype_t *v) * Clear all references associated with vartype . * * void clear_fulltype_ref(fulltype_t *v) * Clear all references associated with fulltype . */ #define clear_vartype_ref(v) \ do{ vartype_t *fvt = v; \ if (fvt->t_struct) clear_struct_type_ref(fvt->t_struct);\ } while(0) #define clear_fulltype_ref(v) \ do{ fulltype_t *fvt = v; \ if (fvt->t_struct) clear_struct_type_ref(fvt->t_struct);\ } while(0) /* void count_vartype_ref(vartype_t *v) * Count all references associated with vartype . * * void count_fulltype_ref(fulltype_t *v) * Count all references associated with fulltype . */ #define count_vartype_ref(v) \ do{ vartype_t *fvt = v; \ if (fvt->t_struct) count_struct_type_ref(fvt->t_struct);\ } while(0) #define count_fulltype_ref(v) \ do{ fulltype_t *fvt = v; \ if (fvt->t_struct) count_struct_type_ref(fvt->t_struct);\ } while(0) #endif /* GC_SUPPORT */ #else /* !USE_STRUCTS */ #define ref_vartype_data(v) NOOP #define ref_fulltype_data(v) NOOP #define free_vartype_data(v) NOOP #define free_fulltype_data(v) NOOP #ifdef GC_SUPPORT #define clear_vartype_ref(v) NOOP #define clear_fulltype_ref(v) NOOP #define count_vartype_ref(v) NOOP #define count_fulltype_ref(v) NOOP #endif /* GC_SUPPORT */ #endif /* USE_STRUCTS */ /* --- struct instr_s: description of stackmachine instructions --- * * Stackmachine instructions are both 'internal' codes with no external * representation, as well as efuns. * * The information about all instructions is collected in the table * instrs[] which is indexed by the code of the instructions. * The .prefix and .opcode fields give the proper stackmachine opcodes * for every instruction. * * The table is declared in instrs.h and defined in the file efun_defs.c * both of which are created by make_func from the func_spec file. * The table is compiled into the lexer module and exported from there. */ struct instr_s { bytecode_t prefix; /* 0 or prefix code if required */ bytecode_t opcode; /* The instructions (sub-)opcode */ short max_arg; /* Maximum number of arguments, -1 for '...' */ short min_arg; /* Minimum number of arguments. * The number 0 marks incallable closures: instructions which * are used as syntactic markers in lambda closures, but by * themselves can't be executed. */ short Default; /* An efun to use as default value for last argument. * > 0: index into instrs[] to the efun to use. * 0: no default value. * -1: this whole entry describes an internal stackmachine code, * not a normal efun (an 'operator' in closure lingo). */ fulltype_t ret_type; /* The return type used by the compiler. */ short arg_index; /* Indexes the efun_arg_types[] arrays. */ short lpc_arg_index; /* Indexes the efun_lpc_types[] arrays. */ /* A '-1' index means that no type information * is available. */ char *name; /* The printable name of the instruction. */ char *deprecated; /* Usually NULL, for deprecated efuns this is * the warning message to print. */ }; /* --- Function flags --- * * Programs know about their functions from an array of uint32s which hold * the flags for the function and, in one field in the low bits, the * address of the code. * * Some flags (TYPE_MOD_*) are also used for variable types. * * Using a bitfield is tempting, but generates inefficient code due to the * lack of a real 'bool' datatype. */ typedef uint32 funflag_t; /* Function flags */ #define NAME_INHERITED 0x80000000 /* defined by inheritance */ #define TYPE_MOD_STATIC 0x40000000 /* Static function or variable */ #define TYPE_MOD_NO_MASK 0x20000000 /* The nomask => not redefineable */ #define TYPE_MOD_PRIVATE 0x10000000 /* Can't be inherited */ #define TYPE_MOD_PUBLIC 0x08000000 /* Force inherit through private */ #define TYPE_MOD_VARARGS 0x04000000 /* Used for type checking */ #define VAR_INITIALIZED 0x04000000 /* Variable is not shared */ #define TYPE_MOD_VIRTUAL 0x02000000 /* can be re- and cross- defined */ #define TYPE_MOD_PROTECTED 0x01000000 /* cannot be called externally */ #define TYPE_MOD_XVARARGS 0x00800000 /* accepts optional arguments */ #define TYPE_MOD_NOSAVE 0x00400000 /* vars: can't be saved */ #define FUNSTART_MASK 0x000fffff /* Function not inherited: unsigned address of the function code relative * to the begin of the program block (struct program_s->program). */ #define NAME_CROSS_DEFINED 0x00080000 /* Two functions with the same name inherited from A and B into C. * The compiler usually prefers A, and the value 'flags & INHERIT_MASK' * (in bias-0x20000 representation) stored as B.offset.func is the * difference between to the real functions index (also see below). * A special use is A uses a function from B. The function is marked * in A as undefined, but the compiler will use cross-defining in C * to resolve the function calls in A to call the function in B. */ #define INHERIT_MASK 0x0003ffff /* Function inherited: unsigned index of the parent program descriptor * in the struct program_s->inherit[] array. In the parent program, this * function may again be inherited. * Function crossdefined: signed difference (in bias-0x20000 notation) * from the current function index to the real function index * (real = this + offset). */ #ifdef USE_STRUCTS #define NAME_HIDDEN 0x00020000 /* Not visible for inheritance */ #define NAME_PROTOTYPE 0x00010000 /* Defined by a prototype only */ #define NAME_UNDEFINED 0x00008000 /* Not defined yet */ #define NAME_TYPES_LOST 0x00004000 /* inherited, no save_types */ #else /* !USE_STRUCTS */ #define NAME_HIDDEN 0x00000800 /* Not visible for inheritance */ #define NAME_PROTOTYPE 0x00000400 /* Defined by a prototype only */ #define NAME_UNDEFINED 0x00000200 /* Not defined yet */ #define NAME_TYPES_LOST 0x00000100 /* inherited, no save_types */ #endif /* USE_STRUCTS */ #define CROSSDEF_NAME_OFFSET(flags) \ (((flags) & INHERIT_MASK) - ((INHERIT_MASK + 1) >> 1)) /* For a given NAME_CROSS_DEFINED function, extract the difference to * the real functions index from the flags. */ #define GET_CROSSDEF_OFFSET(value) \ ((value) - ((INHERIT_MASK + 1) >> 1)) /* The index difference in bias-notation is converted back into * the real number. The difference to CROSSDEF_NAME_OFFSET is that * is supposed to be the plain number, not a real function flags word. */ #define MAKE_CROSSDEF_OFFSET(value) \ ((value) + ((INHERIT_MASK + 1) >> 1)) /* Convert the index difference into the bias-notation for storage * in the function flags. */ #define MAKE_CROSSDEF_ROFFSET(value) \ ((value) - ((INHERIT_MASK + 1) >> 1)) /* Convert the reverse index difference into the bias-notation for * storage in the function flags. * TODO: What is this exactly? */ #define SEARCH_FUNCTION_INHERIT(inheritp, progp, fx) \ do{ \ int sf_inum = progp->num_inherited; \ for (inheritp = progp->inherit; \ sf_inum > 0 \ && (fx < inheritp->function_index_offset \ || fx >= inheritp->function_index_offset + inheritp->prog->num_functions); \ inheritp++, sf_inum--); \ } while(0) /* Look for the right inherit index, which contains the function with * the index . It is assumed, that progp has at least one inherit * and that points indeed to a function in an inherit of . */ /* --- Function header --- * * The bytecode for every function is preceeded by a header with the name * and information about arguments and types: * * struct fun_hdr { * shared string_t * name_of_function; (4 Bytes) #ifdef USE_STRUCTS * vartype_t return_type; (6 Byte) * References from the return_type are not counted! #else * vartype_t return_type; (2 Byte) #endif * --> byte number_formal_args; (1 Byte) * Bit 7: set if the function has a 'varargs' argument * TODO: some code makes use of the fact that this makes the number negative * Bit 6..0: the number of arguments * byte number_local_vars; (1 Byte) * This includes the svalues needed for the break stack for * switch() statements. * bytecode_t opcode[...] * } * * The function address given in the program's function block points to * .number_formal_args. * * Since structs introduce uncontrollable padding, access of all fields * is implemented using macros taking the 'function address', typedef'd * as fun_hdr_p, as argument and evaluate to the desired value. * * The whole header structure is aligned properly so that the name_of_function * pointer and the return_type short can be used directly. * * Note: Changes here can affect the struct lambda layout and associated * constants. * TODO: the other fields should have proper types, too. * TODO: the whole information should be in a table, and not in the * TODO:: bytecode. See struct program_s. */ typedef bytecode_p fun_hdr_p; /* TODO: Make this a void* for now? */ #define SIMUL_EFUN_FUNSTART ((bytecode_p) -1) /* Special value used for inter_pc and funstart to mark simul_efuns * for dump_trace(). * TODO: Invent something similar for efun/operator closures? */ #define EFUN_FUNSTART ((bytecode_p) -2) /* Special value used for funstart to mark efuns for dump_trace. */ #define FUNCTION_NAMEP(p) ((void *)((char *)p - sizeof(vartype_t) - sizeof(string_t *))) #define FUNCTION_TYPEP(p) ((void *)((char *)p - sizeof(vartype_t))) #define FUNCTION_NUM_ARGS(p) EXTRACT_SCHAR((char *)p) #define FUNCTION_NUM_VARS(p) (*((unsigned char *)((char *)p + sizeof(char)))) #define FUNCTION_CODE(p) ((bytecode_p)((unsigned char *)p + 2* sizeof(char))) #define FUNCTION_FROM_CODE(p) ((fun_hdr_p)((unsigned char *)p - 2* sizeof(char))) #define FUNCTION_PRE_HDR_SIZE (sizeof(string_t*) + sizeof(vartype_t)) /* Number of function header bytes before the function pointer. */ #define FUNCTION_POST_HDR_SIZE (2 * sizeof(char)) /* Number of function header bytes after the function pointer. */ #define FUNCTION_HDR_SIZE (FUNCTION_PRE_HDR_SIZE + FUNCTION_POST_HDR_SIZE) /* Total size of the function header. */ /* --- struct variable_s: description of one variable * * This structure describes one variable, inherited or own. * The type part of the .flags is used just by the compiler. */ struct variable_s { string_t *name; /* Name of the variable (shared string) */ fulltype_t type; /* Type and visibility of the variable (type object counted). * If a variable is inherited virtually, the function flag * TYPE_MOD_VIRTUAL is or'ed .type.typeflags. */ }; /* --- struct inherit: description of one inherited program * * The information about inherited programs ("objects") for a given * program is stored in an array of these structure, and the inherited * programs are accessed from the childs' program code by indexing this array. */ struct inherit_s { program_t *prog; /* Pointer to the inherited program */ unsigned short function_index_offset; /* Offset of the inherited program's function block within the * inheriting program's function block. */ unsigned short variable_index_offset; /* Offset of the inherited program's variables block within the * inheriting program's variable block. * The NON_VIRTUAL_OFFSET_TAG marks the variables of non-virtual * inherits temporarily during compiles. */ unsigned short inherit_type; /* Type of inherit */ # define INHERIT_TYPE_NORMAL 0x0000 /* Type: Normal inherit */ # define INHERIT_TYPE_VIRTUAL 0x0001 /* Type: Virtual inherit */ # define INHERIT_TYPE_EXTRA 0x0002 /* Type: Extra inherit added by * copy_variables() */ # define INHERIT_TYPE_DUPLICATE 0x0004 /* Flag: Duplicate virt inherit */ unsigned short inherit_depth; /* Depth of inherit */ }; #ifdef USE_STRUCTS /* --- struct struct_def: description of one struct * * The information about structs visible in the program are stored * in an array of these structures; this includes all inherited structs. */ struct struct_def_s { funflag_t flags; /* Visibility */ struct_type_t * type; /* The struct definition itself (counted). */ int inh; /* -1, or the index of the inherit where this * struct came from. */ }; #endif /* USE_STRUCTS */ /* --- struct include_s: description of one include file * * This structure describes one include file used to compile the * program. */ struct include_s { string_t *name; /* Name as it was found in the program (tabled string). First and last * character are the delimiters - either "" or <>. */ string_t *filename; /* Actual filename of the include file, in compat mode * without leading slash (tabled string). */ int depth; /* The absolute value is the include depth, counting from 1 upwards. * If the include did not generate code, the value is stored negative. */ }; /* --- struct program_s: the program head structure * * This structure is actually just the head of the memory block * with all the programs data. */ /* TODO: We seem to need a datatype for program offsets (right now: unsigned short). * TODO:: It shows up in quite a lot of places. * TODO: Replace the on program_s structure with a header/data couple. The * TODO:: header keeps vars likes refs, blueprint, swap_num, *data; and the * TODO:: data keeps the static bytecodes and similar. This way we can swap * TODO:: the program even for clones. */ struct program_s { p_int ref; /* Reference count */ p_int total_size; /* Total size of the memory block allocated for this program. */ #ifdef DEBUG p_int extra_ref; /* Used to verify ref count */ #endif bytecode_p program; /* The binary instructions */ string_t *name; /* Name of file that defined prog (untabled, no leading '/', * but a trailing '.c', no embedded '\0') */ object_t *blueprint; /* Counted pointer to the (possibly destructed) blueprint object, * or NULL if the blueprint has been destructed in a previous cycle. * Note: Whenever this member is changed, make sure to remove * the program from the swap ('remove_prog_swap(prog, MY_TRUE)'). */ int32 id_number; /* The id-number is unique among all programs and used to store * information for this program without actually pointing to * the structure. */ mp_int load_time; /* When has it been compiled ? */ linenumbers_t *line_numbers; /* Line number information, NULL when not swapped in. * If swapped out, the data is stored in the swap file at * .swapnum+.total_size . */ unsigned short *function_names; #define PROGRAM_END(program) ((bytecode_p)(program).function_names) /* Lookup table [.num_function_names] function-index -> offset of * the function within the functions[] table. function_names[] is * stored right after the the bytecode within the same allocation * unit. * The table is sorted in descending order of the pointers(!) * of the shared function name strings. If the program contains * redefinitions of inherited functions, the entry here points * to the redefinition, the inherited function can then be * found from there. */ funflag_t *functions; /* Array [.num_functions] with the flags and addresses of all * functions, inherited and own. * Nameless functions (those without an entry in function_names[]) * are collected at the end of the table. * TODO: Instead of hiding the function information in the bytecode * TODO:: it should be tabled here. */ string_t **strings; /* Array [.num_strings] of the shared strings used by the program. * Stored in reverse order at the end of the array are the pointers * to the names of all included files which generated code - these * are used when retrieving line numbers. */ variable_t *variables; /* Array [.num_variables] with the flags, types and names of all * variables. */ inherit_t *inherit; /* Array [.num_inherited] of descriptors for (directly) inherited programs. */ include_t *includes; /* Array [.num_includes] of descriptors for included files. */ #ifdef USE_STRUCTS struct_def_t *struct_defs; /* Array [.num_structs] of struct descriptors */ #endif /* USE_STRUCTS */ unsigned short flags; /* Flags for the program: */ # define P_REPLACE_ACTIVE 0x0001 /* This program will be or has been replaced at least once. * As this flag is never reset, the caller must check the * obj_list_replace if his object is affected or not. */ # define P_NO_INHERIT 0x0002 /* Program must not be inherited */ # define P_NO_CLONE 0x0004 /* No clones allowed */ # define P_NO_SHADOW 0x0008 /* No shadows allowed */ # define P_SHARE_VARIABLES 0x0010 /* Clone vars are assigned from * the current blueprint vars. */ short heart_beat; /* Index of the heart beat function. -1 means no heart beat */ /* The types of all function arguments are saved in the * .argument_types[]. To look up the arguments types for * function , retrieve the start index from the .type_start[] * as .type_start[n]. If this index is INDEX_START_NONE, the function * has no type information. * * Both arrays will only be allocated if '#pragma save_types' has * been specified. */ vartype_t *argument_types; unsigned short *type_start; /* TODO: Some code relies on this being unsigned short */ # define INDEX_START_NONE 65535 p_int swap_num; /* The swap number (swap file offset) for an unswapped program * It is set to -1 if it hasn't been swapped yet. */ /* * And now some general size information. */ unsigned short num_function_names; /* Number of function names listed in the lookup table. * This number is <= .num_functions as the redefinition of * of inherited functions does not need an additional name * entry. Also, private functions have no visible name. */ unsigned short num_functions; /* Number of functions (inherited and own) of this program */ unsigned short num_strings; /* Number of shared strings (including filenames) used by the program */ unsigned short num_includes; /* Number of stored include filenames */ unsigned short num_variables; /* Number of variables (inherited and own) of this program */ unsigned short num_inherited; /* Number of (directly) inherited programs */ #ifdef USE_STRUCTS unsigned short num_structs; /* Number of listed struct definitions */ #endif /* USE_STRUCTS */ }; /* --- struct linenumbers_s: the linenumber head structure * * This structure is the head of the memory block with the linenumbers * data. */ struct linenumbers_s { size_t size; /* Total allocated size of this structure */ bytecode_t line_numbers[1]; /* Array [.size - sizeof(.size)] with the delta-compressed * line number information. This is actually one byte too many, but * that simplifies the swapping code. */ }; /* --- struct function_s: Function description * * Structures of this type hold various important pieces of * information about a function. * The compiler uses this structure to collect the function information * of newly defined and inherited functions, of which the former will also * be compiled into the function header * The simul_efun module uses this structure to look up quickly functions. */ struct function_s { string_t *name; /* Name of function (shared string) */ union { uint32 pc; /* lfuns: Address of function header */ uint32 inherit; /* Inherit table index from where inherited. */ int32 func; /* For cross-defined functions, this is the index offset * to the original function in bias-0x200000 notation. * Semantik: real-index = this-index + offset. * The offset is also stored in the function flags in * the program_t.functions[] array. * * simul_efun.c also uses this field as a 'next' * index in the simul_efun function table for * functions that have been discarded due to a * change in the number of arguments. */ function_t *next; /* used for mergesort */ } offset; funflag_t flags; /* Function flags */ fulltype_t type; /* Return type of function (counted). */ unsigned char num_local; /* Number of local variables */ unsigned char num_arg; /* Number of arguments needed. */ # define SIMUL_EFUN_VARARGS 0xff /* Magic num_arg value for varargs */ }; /* --- Bytecodes used to encode line numbers --- * * The line number information is a block of bytecode associating small * blocks of bytecode with the generating line number ranges. * * To save space, the information uses a delta compression, necessitating * to read all information from the beginning and counting up a line number * and bytecode offset counter to retrieve a specific bytecode/line number * association. * * The names of included files are stored in the order of appearance * at the end of the string table. Multiple included files are stored * several times. */ /* Bytecodes 0x00..0x3a: The amount of program bytes generated for the * current line; this entry is complete with that. */ #define LI_MAXOFFSET 0x3b /* The current line generated 0x3b bytes (more), but the entry * is not complete. This code used after the linecodes 0xC0..0xFF. */ #define LI_BACK 0x3c /* Followed by unsigned byte . * The current line counter was set back by +1. */ #define LI_INCLUDE 0x3d /* The following program bytes were generated from the next included * file. Reset the current line number to 0. * LI_INCLUDEs can be nested. */ #define LI_INCLUDE_END 0x3e /* End of the included program part. */ #define LI_L_RELOCATED 0x3f /* Followed by bytes LI_L_RELOCATED * The lines were relocated from a line by a delta to the current_line-2. * The delta, an signed int16, is encoded in and . */ /* Bytecodes 0x40..0x7f encode actual code/line number relations: * codesize: 1..8 * line incr: 2..9 * -> bytecode = (lineincr+6) << 3 | (codesize-1) * Each code is a complete entry. */ #define LI_RELOCATED 0xc0 /* Bytecodes 0x80..0xDF: a small line relocation. * The lines were relocated from a line by a delta -0x20..0x1F * to the current_line-2. * The delta is encoded as delta+LI_RELOCATED. */ #define LI_SMALL_REL 0x20 /* The allowed magnitude of a relocation to fit into a small * relocation code. */ #define LI_MAXEMPTY 0x20 /* Bytecodes 0xE0..0xFF: 'use' a number of lines between 1 and 32 * -> bytecode = (0x100 - lines) * The entry is not complete. */ /***************************************************************************/ #endif /* EXEC_H__ */