psyclpc/src/exec.h

995 lines
36 KiB
C

#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 <v>.
*
* void ref_fulltype_data(fulltype_t *v)
* Add another reference to the data associated with fulltype <v>.
*/
#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 <v>.
*
* void free_fulltype_data(fulltype_t *v)
* Free all data associated with fulltype <v>.
*/
#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 <v>.
*
* void clear_fulltype_ref(fulltype_t *v)
* Clear all references associated with fulltype <v>.
*/
#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 <v>.
*
* void count_fulltype_ref(fulltype_t *v)
* Count all references associated with fulltype <v>.
*/
#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 <value> in bias-notation is converted back into
* the real number. The difference to CROSSDEF_NAME_OFFSET is that <value>
* 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 <value> 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 <value> 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 <fx>. It is assumed, that progp has at least one inherit
* and that <fx> points indeed to a function in an inherit of <progp>.
*/
/* --- 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 <n>, 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 <off>.
* The current line counter was set back by <off>+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 <hi> <lo> 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 <hi> and <lo>.
*/
/* 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__ */