Files
mercury/bytecode/mb_module.c
Levi Cameron 49438027b7 Major changes to bytecode interpreter.
Estimated hours taken: 200

Major changes to bytecode interpreter.
Beginnings of native code integration

bytecode/bytecode.c:
bytecode/bytecode.h:
bytecode/dict.c:
bytecode/dict.h:
bytecode/disasm.c:
bytecode/disasm.h:
bytecode/machine.c:
bytecode/machine.h:
bytecode/mbi_main.c:
bytecode/mdb.m:
bytecode/mem.c:
bytecode/mem.h:
bytecode/slist.c:
bytecode/slist.h:
bytecode/template.c:
bytecode/template.h:
bytecode/util.c:
bytecode/util.h:
        Removed. These are all the old bytecode files from
        before I started. Any parts that were useful have already
        been salvaged and used in the new interpreter.

bytecode/*:
	Added MB_Bytecode_Addr and MB_Native_Addr types to remove abiguity
	as to what type of code an instruction pointer points to, and
	provide compiler help for erroneously mixing pointer types.

bytecode/Mmakefile:
bytecode/Mmake.params:
        Makefile for test bytecode program. Note that any library
        functions that are called from bytecode must be compiled
        with trace information. (So their entry labels can be
        looked up)

bytecode/mb_basetypes.h:
        Added. Contains basic type definitions.

bytecode/mb_bytecode.c:
bytecode/mb_bytecode.h:
        Better error messages.
        Changed var_lists to direct pointers rather than
        lookups through data stacks (much simpler but stop you
        using realloc() on the bytecode argument data)
        Label addresses are computed at module load time rather
        than being looked up each jump
        Added endof_negation_goal
        Temporary stack slot numbers are translated to variable
        numbers (now there is no distinction between temps & vars)
        MB_read_cstring return value convention changed (see comments
	for how to now free the returned memory)
        Added distinction between functions and predicates
        Added enter_else
        Code addresses are all pointers rather than simple ints
	Added MB_Code_Addr type for pred_const and call instructions

bytecode/mb_disasm.c:
bytecode/mb_disasm.h:
        Added endof_negation_goal & enter_else
        Output strings are now easier to read
        MB_listing does not display anything for invalid addresses
        MB_listing takes line length argument

bytecode/mb_interface.c:
bytecode/mb_interface.h:
bytecode/mb_interface_stub.m:
        Interfacing between native/bytecode

bytecode/mb_machine.c:
bytecode/mb_machine.h:
bytecode/mb_machine_def.h:
        Large sections of code branched off into mb_module.?
        Most instructions completed, but not integrated with native
        code.
        Most of mb_machine_def has been removed as the native
        code functions provide the same functionality.

bytecode/mb_machine_show.c:
bytecode/mb_machine_show.h:
        Completely changed. Less information now as a lot of what
        was being displayed before cannot be determined as easily
        now that it is stored in the mercury runtime.

bytecode/mb_mem.c:
bytecode/mb_mem.h:
        Added routines for garbage collected memory

bytecode/mb_module.c:
bytecode/mb_module.h:
        Loading & accessing bytecode. Argument data indexes & id are now
        stored in a single word. (see MB_BCID_xxx macros).
        Call & label addresses are now calculated at load time.

bytecode/mb_stack.c:
bytecode/mb_stack.h:
        Added options for garbage collection of MB_Stack memory

bytecode/mb_util.c:
bytecode/mb_util.h:
        Miscellaneous string functions added and SAY() for debugging

bytecode/simple01.m:
        Added. Simple test program. (replace with whatever
        program is being tested at the time).
2001-02-01 05:20:32 +00:00

938 lines
24 KiB
C

/*
** Copyright (C) 2000-2001 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
**
*/
/*
#include "mercury_layout_util.h"
#include "mercury_array_macros.h"
#include "mercury_getopt.h"
#include "mercury_trace.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_declarative.h"
#include "mercury_trace_alias.h"
#include "mercury_trace_help.h"
#include "mercury_trace_browse.h"
#include "mercury_trace_spy.h"
#include "mercury_trace_tables.h"
#include "mercury_trace_util.h"
#include "mercury_trace_vars.h"
#include "mercury_trace_readline.h"
*/
#include "mb_module.h"
#include "mb_interface.h"
#include <assert.h>
#include <string.h>
#include "mb_mem.h"
/* XXX: We should remove these fixed limits */
#define MAX_CODE_COUNT 10000
#define MAX_CODE_DATA_COUNT 160000
#define MAX_MODULES 64
/*
** File version (simple check for correct bytecode file format)
** Should be the same as that in bytecode.m
*/
#define FILEVERSION 9
/* Exported definitions */
/* Local declarations */
/*
** The bytecodes consist of a sequence of words with each containing the
** bytecode and an index into the code_arg_data array, the data at that
** index being the argument for the bytecode
**
** The bytecode id is just a byte with the uppermost bit being used to
** indicate whether the code executing is in a nondet or det procedure
** (this is needed to work out which stack the var list is on)
**
** If you change this, ensure the bytecodes in mb_bytecode.h will
** still fit in the number of bits allocated to an id
**
** XXX: Can only handle 64MB of bytecode data
*/
#if 0
#define MB_BCID_MAKE(id, arg) ( ((id) & ((1 << CHAR_BIT) - 1)) | \
(((MB_Word*)(arg) - code_arg_data) << CHAR_BIT)\
)
/* get the bytecode id */
#define MB_BCID_ID(x) ((x) & ((1<<(CHAR_BIT-1)) - 1))
/* get the determinism flag for the given bytecode */
#define MB_BCID_ISDET ((1) << (CHAR_BIT-1))
#define MB_BCID_DET(x) ((x) & MB_BCID_ISDET)
/* get the bytecode argument pointer */
#define MB_BCID_ARG(x) ((MB_Bytecode_Arg *) \
(code_arg_data + \
((MB_Unsigned)(x) >> CHAR_BIT)) \
)
#else
#define MB_BCID_MAKE(dest, new_id, new_arg) \
((dest).id = (new_id), \
(dest).is_det = 0, \
(dest).arg = (((MB_Word *) (new_arg) - code_arg_data)), \
(dest))
#define MB_BCID_ID(x) ((x).id)
#define MB_BCID_ISDET 1
#define MB_BCID_DET_GET(x) ((x).is_det)
#define MB_BCID_DET_SET(x, det) ((x).is_det = (det))
#define MB_BCID_ARG(x) ((MB_Bytecode_Arg *) (code_arg_data + (x).arg))
#endif
/* XXX: not thread safe */
static MB_Word code_count = 0;
static MB_BCId code_id[MAX_CODE_COUNT];
#define CODE_DATA_NONE 0 /* 0 is reserved for indicating no data */
static MB_Word code_data_count = 1;
static MB_Word code_arg_data[MAX_CODE_DATA_COUNT];
struct MB_Module_Struct {
/* XXX: Hash the module & predicate names */
/* The name of the module */
MB_CString module_name;
/*
** The following should not be directly accessed unless
** absolutely necessary; use one of the (many) wrapper functions
*/
/*
** The code indices of all the predicates in this module
** If this is empty, then it means we tried to load
** the module but we couldn't find bytecodes for it
*/
/* XXX: This really should be hashed too */
MB_Stack pred_index_stack;
};
/* XXX: The current accesses to these variables are thread safe */
static MB_Word module_count = 0;
static MB_Module *module_arr[MAX_MODULES];
static MB_Bool translate_calls(MB_Bytecode_Addr bc, MB_Unsigned number_codes);
static MB_Bool translate_labels(MB_Bytecode_Addr bc, MB_Unsigned number_codes,
MB_Stack *label_stack);
static MB_Bool translate_detism(MB_Bytecode_Addr bc, MB_Unsigned number_codes);
static MB_Bool translate_switch(MB_Bytecode_Addr bc, MB_Unsigned number_codes);
static MB_Bool translate_temps(MB_Bytecode_Addr bc, MB_Unsigned number_codes);
/* Implementation */
/*
** Translates calls from a predicate name/procedure to an actual code address
** Translates call & higher_order(pred_const) bytecodes
** Returns TRUE if successful
*/
static MB_Bool
translate_calls(MB_Bytecode_Addr bc, MB_Unsigned number_codes)
{
/*
** XXX: We should temporarily table the procs, instead of re-searching
** each time, but since there is usually only one proc per predicate,
** don't bother for now
*/
MB_Unsigned i;
for (i = 0; i < number_codes; i++, bc++) {
/* proc to be called attributes */
MB_CString module_name = NULL;
MB_CString pred_name = NULL;
MB_Word arity;
MB_Bool is_func;
MB_Word mode_num;
/* location to store the proc to be called */
MB_Code_Addr *target_addr = NULL;
/* Get the information about the procedure to call */
MB_Byte call_id = MB_code_get_id(bc);
if (call_id == MB_BC_call) {
MB_Bytecode_Arg *call_arg = MB_code_get_arg(bc);
module_name = call_arg->call.module_name;
arity = call_arg->call.arity;
is_func = call_arg->call.is_func;
pred_name = call_arg->call.pred_name;
mode_num = call_arg->call.mode_num;
target_addr = &call_arg->call.addr;
} else if (call_id == MB_BC_construct) {
MB_Bytecode_Arg *construct_arg =
MB_code_get_arg(bc);
if (construct_arg->construct.consid.id ==
MB_CONSID_PRED_CONST)
{
MB_fatal("Unable to translate predicate constructs");
#if 0
module_name = construct_arg->construct.
consid.opt.pred_const.module_name;
arity = construct_arg->construct.
consid.opt.pred_const.arity;
is_func = construct_arg->construct.
consid.opt.pred_const.is_func;
pred_name = construct_arg->construct.
consid.opt.pred_const.pred_name;
mode_num = construct_arg->construct.
consid.opt.pred_const.mode_num;
target_addr = &construct_arg->construct.
consid.opt.pred_const.addr;
#endif
}
}
if (pred_name != NULL) {
MB_SAY("Looking for %s %s__%s/%d mode %d",
(is_func) ? "func" : "pred",
module_name,
pred_name,
arity,
mode_num);
}
/* Find the predicate start */
if (pred_name != NULL) {
/* First check if we can find it in the bytecode */
MB_Bytecode_Addr bc_addr = MB_code_find_proc(module_name,
pred_name, mode_num,
arity, is_func);
if (bc_addr == MB_CODE_INVALID_ADR) {
/* Otherwise look in the native code */
MB_Native_Addr native_addr =
MB_code_find_proc_native(module_name,
pred_name, mode_num, arity, is_func);
MB_SAY(" Not found in bytecode");
MB_SAY(" Address from native: %08x"
, native_addr);
if (native_addr == NULL) {
MB_util_error(
"Warning: proc ref in bytecode"
" at %08x to unknown"
" %s %s__%s/%d mode %d"
" (will evaluate lazily)",
(int) i,
is_func ? "func" : "pred",
module_name,
pred_name,
(int) arity,
(int) mode_num);
MB_util_error("Are you sure the module"
" was compiled with trace"
" information enabled?");
}
target_addr->is_native = TRUE;
target_addr->addr.native = native_addr;
} else {
target_addr->is_native = FALSE;
target_addr->addr.bc = bc_addr;
}
}
}
return TRUE;
} /* translate_calls */
/*
** Translates labels to code addresses for those instructions that need it
** those translated are:
** enter_if, endof_then, enter_disjunction, enter_disjunct, endof_disjunct
** enter_switch, enter_switch_arm, endof_switch_arm, enter_negation, enter_proc
** Returns TRUE if successful
*/
/* Helper function for translate_labels: translates an invididual label */
static void translate_label(MB_Bytecode_Arg* cur_proc_arg,
MB_Stack* label_stack, MB_Label* label)
{
if (label->index < cur_proc_arg->enter_proc.label_count
&& label->index > 0) {
label->addr = (MB_Bytecode_Addr) MB_stack_peek(label_stack,
cur_proc_arg->enter_proc.label_index + label->index);
} else {
label->addr = MB_CODE_INVALID_ADR;
}
}
static MB_Bool
translate_labels(MB_Bytecode_Addr bc, MB_Unsigned number_codes,
MB_Stack *label_stack)
{
MB_Unsigned i;
MB_Bytecode_Arg *cur_proc_arg = NULL;
for (i = 0; i < number_codes; i++, bc++) {
MB_Bytecode_Arg *cur_arg =
MB_code_get_arg(bc);
switch (MB_code_get_id(bc)) {
case MB_BC_enter_proc:
cur_proc_arg = cur_arg;
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_proc.end_label);
break;
case MB_BC_enter_if:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_if.else_label);
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_if.end_label);
break;
case MB_BC_endof_then:
translate_label(cur_proc_arg, label_stack,
&cur_arg->endof_then.follow_label);
break;
case MB_BC_enter_disjunction:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_disjunction.end_label);
break;
case MB_BC_enter_disjunct:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_disjunct.next_label);
break;
case MB_BC_endof_disjunct:
translate_label(cur_proc_arg, label_stack,
&cur_arg->endof_disjunct.end_label);
break;
case MB_BC_enter_switch:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_switch.end_label);
break;
case MB_BC_enter_switch_arm:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_switch_arm.next_label);
break;
case MB_BC_endof_switch_arm:
translate_label(cur_proc_arg, label_stack,
&cur_arg->endof_switch_arm.end_label);
break;
case MB_BC_enter_negation:
translate_label(cur_proc_arg, label_stack,
&cur_arg->enter_negation.end_label);
break;
}
}
return TRUE;
} /* translate_labels */
/*
** Store the procedure's determinism that each instruction is executing under
** This is used when returning into a procedure to decide whether the
** vars & temps are on the det or nondet stack
** Returns TRUE if successful
*/
static MB_Bool
translate_detism(MB_Bytecode_Addr bc, MB_Unsigned number_codes)
{
MB_Unsigned i;
MB_Byte bc_id;
MB_Byte cur_detism = MB_BCID_ISDET;
for (i = 0; i < number_codes; i++, bc++) {
bc_id = MB_code_get_id(bc);
if (bc_id == MB_BC_enter_proc) {
switch (MB_code_get_arg(bc)->enter_proc.det) {
case MB_DET_DET:
case MB_DET_SEMIDET:
cur_detism = MB_BCID_ISDET;
break;
case MB_DET_MULTIDET:
case MB_DET_NONDET:
cur_detism = 0;
break;
case MB_DET_UNUSABLE:
cur_detism = 0;
break;
default:
assert(FALSE);
}
}
if (cur_detism) {
MB_BCID_DET_SET(*bc, cur_detism);
}
if (bc_id == MB_BC_endof_proc) cur_detism = 0;
}
return TRUE;
} /* translate_detism */
/*
** Fill in the variable that each switch arm is using
** Returns TRUE if successful
*/
static MB_Bool
translate_switch(MB_Bytecode_Addr bc, MB_Unsigned number_codes)
{
MB_Unsigned i;
MB_Bytecode_Arg *cur_switch = NULL;
for (i = 0; i < number_codes; i++, bc++) {
switch (MB_code_get_id(bc)) {
case MB_BC_enter_switch:
cur_switch = MB_code_get_arg(bc);
break;
case MB_BC_enter_switch_arm: {
MB_Bytecode_Arg *cur_arg
= MB_code_get_arg(bc);
cur_arg->enter_switch_arm.var =
cur_switch->enter_switch.var;
break;
}
}
}
return TRUE;
} /* translate_switch */
/*
** Transform temporary stack slot numbers into variable slot numbers
** for all bytecodes that use a temporary stack slot
** Returns TRUE if successful
*/
#define XLATTEMP(name) case MB_BC_##name: \
cur_arg = MB_code_get_arg(bc); \
cur_arg->name.frame_ptr_tmp += \
cur_proc_arg->enter_proc.list_length; \
break;
static MB_Bool
translate_temps(MB_Bytecode_Addr bc, MB_Unsigned number_codes)
{
MB_Unsigned i;
MB_Bytecode_Arg *cur_arg ;
MB_Bytecode_Arg *cur_proc_arg = NULL;
MB_Word code_size = MB_code_size();
for (i = 0; i < number_codes; i++, bc++) {
switch (MB_code_get_id(bc)) {
case MB_BC_enter_proc:
cur_proc_arg = MB_code_get_arg(bc);
break;
XLATTEMP(enter_if);
XLATTEMP(enter_then);
XLATTEMP(enter_negation);
XLATTEMP(endof_negation_goal);
XLATTEMP(enter_commit);
}
}
return TRUE;
}
/*
** Load a module by name. Assumes the bytecode file is just the module name
** with '.mbc' appended.
*/
MB_Module *
MB_module_load_name(MB_CString_Const module_name)
{
MB_Module *module;
MB_CString filename = MB_str_new_cat(module_name, ".mbc");
FILE *fp = fopen(filename, "rb");
module = MB_module_load(module_name, fp);
MB_str_delete(filename);
return module;
} /* MB_module_load_name */
/*
** Gets a module. Loads the module if it is not already loaded.
** If there is no bytecode information for this module, returns NULL
*/
MB_Module *
MB_module_get(MB_CString_Const module_name)
{
/* Search for the module */
MB_Word i;
MB_SAY(" Looking for %s among %d modules", module_name, module_count);
for (i = 0; i < module_count; i++) {
MB_SAY(" Testing module %d", i);
if (!MB_str_cmp(module_name, module_arr[i]->module_name)) {
MB_SAY(" Module %s found", module_name);
return module_arr[i];
}
}
MB_SAY(" module %s not found, attempting to load", module_name);
/* We didn't find it so load it */
return MB_module_load_name(module_name);
} /* MB_module_get */
#define MB_ARGSIZE_WORDS(name) \
MB_NUMBLOCKS(sizeof(((MB_Bytecode *)NULL)->opt.name), sizeof(MB_Word))
/* XXX ORDER */
/* the size of the arguments in a MB_Bytecode struct, in number of MB_Words */
static const MB_Word argument_size[] = {
MB_ARGSIZE_WORDS(enter_pred),
MB_ARGSIZE_WORDS(endof_pred),
MB_ARGSIZE_WORDS(enter_proc),
MB_ARGSIZE_WORDS(endof_proc),
MB_ARGSIZE_WORDS(label),
MB_ARGSIZE_WORDS(enter_disjunction),
MB_ARGSIZE_WORDS(endof_disjunction),
MB_ARGSIZE_WORDS(enter_disjunct),
MB_ARGSIZE_WORDS(endof_disjunct),
MB_ARGSIZE_WORDS(enter_switch),
MB_ARGSIZE_WORDS(endof_switch),
MB_ARGSIZE_WORDS(enter_switch_arm),
MB_ARGSIZE_WORDS(endof_switch_arm),
MB_ARGSIZE_WORDS(enter_if),
MB_ARGSIZE_WORDS(enter_then),
MB_ARGSIZE_WORDS(endof_then),
MB_ARGSIZE_WORDS(endof_if),
MB_ARGSIZE_WORDS(enter_negation),
MB_ARGSIZE_WORDS(endof_negation),
MB_ARGSIZE_WORDS(enter_commit),
MB_ARGSIZE_WORDS(endof_commit),
MB_ARGSIZE_WORDS(assign),
MB_ARGSIZE_WORDS(test),
MB_ARGSIZE_WORDS(construct),
MB_ARGSIZE_WORDS(deconstruct),
MB_ARGSIZE_WORDS(complex_construct),
MB_ARGSIZE_WORDS(complex_deconstruct),
MB_ARGSIZE_WORDS(place_arg),
MB_ARGSIZE_WORDS(pickup_arg),
MB_ARGSIZE_WORDS(call),
MB_ARGSIZE_WORDS(higher_order_call),
MB_ARGSIZE_WORDS(builtin_binop),
MB_ARGSIZE_WORDS(builtin_unop),
MB_ARGSIZE_WORDS(builtin_bintest),
MB_ARGSIZE_WORDS(builtin_untest),
MB_ARGSIZE_WORDS(semidet_succeed),
MB_ARGSIZE_WORDS(semidet_success_check),
MB_ARGSIZE_WORDS(fail),
MB_ARGSIZE_WORDS(context),
MB_ARGSIZE_WORDS(not_supported),
MB_ARGSIZE_WORDS(enter_else),
MB_ARGSIZE_WORDS(endof_negation_goal)
}; /* argument_size */
/*
** Load a module
** If fp is NULL then that means there is no bytecode information
** for this module -- revert to native code, and mark the module
** as native code only
*/
MB_Module *MB_module_load(MB_CString_Const module_name, FILE *fp)
{
MB_Short version;
MB_Word module_code_count = 0;
MB_Bytecode_Addr module_start = code_id + code_count;
/* Array of indexes for label translation (used only during load) */
MB_Stack label_stack = MB_stack_new(128, FALSE);
/* Create the new module */
MB_Module *module = MB_GC_NEW(MB_Module);
module->pred_index_stack= MB_stack_new((fp == NULL) ? 0 : 64, FALSE);
module->module_name = MB_str_dup(module_name);
/* XXX Adding to the array like this is not thread safe */
if (module_count >= MAX_MODULES) {
MB_fatal("Too many modules");
}
module_arr[module_count++] = module;
if (fp == NULL) return module;
/* Check the file version is ok */
if (!MB_read_bytecode_version_number(fp, &version)) {
MB_util_error("Unable to read version number\n");
return NULL;
}
if (version != FILEVERSION) {
MB_util_error("Unknown file format version\n");
return NULL;
}
{
MB_Bytecode bc;
MB_Bytecode_Arg *cur_proc_arg = NULL;
MB_Bytecode_Addr cur_proc = MB_CODE_INVALID_ADR;
/* read in each bytecode */
while (MB_read_bytecode(fp, &bc)) {
MB_Bytecode_Arg *cur_arg;
if (bc.id == MB_BC_label) {
/*
** XXX: we strictly don't actually need to save the
** labels but it makes label translations a lot faster.
** After translation, the label stack is deleted
*/
if (cur_proc_arg == NULL) {
MB_fatal("Label outside proc\n");
}
/* Add the label to the current proc's list of labels */
MB_stack_poke(&label_stack,
cur_proc_arg->enter_proc.label_index
+ bc.opt.label.label,
(MB_Word)(code_id + code_count));
} else if (bc.id == MB_BC_not_supported) {
/*
** We came across unsupported code. Mark this proc as
** unusable
*/
if (cur_proc_arg == NULL) {
MB_fatal("Code outside proc\n");
}
cur_proc_arg->enter_proc.det = MB_DET_UNUSABLE;
}
/*
** Copy the bytecode arguments into the code.data
** structure, save the index & increment code.data
** counters
*/
if (bc.id < sizeof(argument_size)/sizeof(argument_size[0])) {
if (argument_size[bc.id] == 0) {
/* If bytecode has no arguments, skip alloc */
cur_arg = NULL;
} else {
/* Allocate space for bytecode's arguments */
cur_arg = MB_CODE_DATA_ALLOC(MB_Bytecode_Arg,
argument_size[bc.id]);
/* Copy arguments onto argument data stack */
memcpy(cur_arg,
&(bc.opt),
argument_size[bc.id]*sizeof(MB_Word));
/* Check if we just entered/exited a procedure*/
switch (bc.id) {
case MB_BC_enter_proc:
/*
** Save the new current proc (so
** labels know where they are)
*/
cur_proc = code_id + code_count;
cur_proc_arg = cur_arg;
/*
** and mark where the label indexes
** will begin
*/
cur_proc_arg->enter_proc.label_index =
MB_stack_size(&label_stack);
MB_stack_alloc(&label_stack,
cur_proc_arg->
enter_proc.label_count);
break;
case MB_BC_endof_proc: {
/* Save the proc we were in */
cur_arg->endof_proc.proc_start =
cur_proc;
cur_proc_arg = NULL;
break;
}
case MB_BC_enter_pred:
MB_stack_push(&module->pred_index_stack,
code_count);
break;
}
}
/* Write bytecode id & argument index */
MB_BCID_MAKE(code_id[code_count], bc.id, cur_arg);
} else {
MB_util_error("Unknown op code");
MB_module_unload(module);
MB_stack_delete(&label_stack);
return NULL;
}
code_count++;
module_code_count++;
}
}
if (feof(fp) &&
(module_code_count > 0) &&
(translate_labels(module_start, module_code_count,
&label_stack)) &&
(translate_calls(module_start, module_code_count)) &&
(translate_detism(module_start, module_code_count)) &&
(translate_switch(module_start, module_code_count)) &&
(translate_temps(module_start, module_code_count)))
{
/* Delete the label stack (we've done all the translations) */
MB_stack_delete(&label_stack);
return module;
} else {
MB_fatal("Error reading bytecode file");
}
return NULL;
} /* MB_module_load */
/*
** Free memory associated with module structure itself
** (does not unload bytecodes from code array, since other
** modules may have been loaded on top of this one)
**
** XXX: Should add code to unload all modules and reload
** only the ones needed, thus effectively unloading a
** given module
*/
void
MB_module_unload(MB_Module *module)
{
if (module != NULL) {
/*
** The stacks will always be allocated since it will
** have aborted if their allocation failed
*/
MB_str_delete(module->module_name);
MB_stack_delete(&module->pred_index_stack);
MB_GC_free(module);
}
}
/* Get the actual size of a program, in bytecodes */
MB_Unsigned
MB_code_size(void)
{
return code_count;
}
/* Get the bytecode type at a given address */
MB_Byte
MB_code_get_id(MB_Bytecode_Addr addr)
{
if (!MB_ip_normal(addr))
return MB_BC_debug_invalid;
/* return the code with the determinism flag stripped away */
return MB_BCID_ID(*addr);
}
/* Get a bytecode's procedure's determinism */
MB_Byte
MB_code_get_det(MB_Bytecode_Addr addr)
{
assert(MB_ip_normal(addr));
/* return the determinism flag */
return (MB_BCID_DET_GET(*addr) == MB_BCID_ISDET)
? MB_ISDET_YES : MB_ISDET_NO;
}
/* Get the bytecode argument at a given address */
MB_Bytecode_Arg *
MB_code_get_arg(MB_Bytecode_Addr addr)
{
MB_Bytecode_Arg *data_p;
if (!MB_ip_normal(addr)) return NULL;
data_p = MB_BCID_ARG(*addr);
if (data_p == (MB_Bytecode_Arg *)code_arg_data) {
return NULL;
} else {
return data_p;
}
} /* MB_code_get_arg */
MB_Bytecode_Addr
MB_code_get_pred_addr(MB_Bytecode_Addr addr) {
while (MB_code_get_id(addr) != MB_BC_enter_pred) {
addr--;
if (!MB_ip_normal(addr)) {
return MB_CODE_INVALID_ADR;
}
}
return addr;
}
MB_Bytecode_Addr
MB_code_get_proc_addr(MB_Bytecode_Addr addr)
{
MB_Byte bc_id;
addr++;
do {
addr--;
assert(MB_ip_normal(addr));
bc_id = MB_code_get_id(addr);
assert(bc_id != MB_BC_enter_pred);
assert(bc_id != MB_BC_endof_pred);
} while (bc_id != MB_BC_enter_proc);
return addr;
} /* MB_code_get_proc_addr */
/* Finds the location of a given proc */
MB_Bytecode_Addr
MB_code_find_proc(MB_CString_Const module_name,
MB_CString_Const pred_name, MB_Word mode_num,
MB_Word arity, MB_Bool is_func)
{
MB_Bytecode_Addr addr;
MB_Word size;
MB_Module *module = MB_module_get(module_name);
MB_Word j;
MB_SAY(" Looking for %s %s__%s/%d mode %d",
(is_func) ? "func" : "pred",
module_name, pred_name, arity, mode_num);
if (MB_stack_size(&module->pred_index_stack) == 0) {
MB_SAY(" No bytecode information for this module");
return MB_CODE_INVALID_ADR;
}
size = MB_stack_size(&module->pred_index_stack);
for (j = 0; j < size; j++) {
MB_Bytecode_Arg *pred_arg;
addr = code_id + MB_stack_peek(&module->pred_index_stack, j);
pred_arg = MB_code_get_arg(addr);
if ((pred_arg->enter_pred.pred_arity
== arity)
&& (pred_arg->enter_pred.is_func
== is_func)
&& MB_str_cmp(pred_arg->
enter_pred.pred_name,
pred_name) == 0)
{
break;
}
}
/* Check if any of the predicates matched */
if (j == MB_stack_size(&module->pred_index_stack)) {
MB_SAY(" Not found");
return MB_CODE_INVALID_ADR;
}
/* one obviously did */
/* Now find the right proc */
do {
MB_Byte bc_id;
addr++;
assert(MB_ip_normal(addr));
bc_id = MB_code_get_id(addr);
if (bc_id == MB_BC_enter_proc) {
MB_Bytecode_Arg *proc_arg = MB_code_get_arg(addr);
if (proc_arg->enter_proc.mode_num == mode_num &&
proc_arg->enter_proc.det != MB_DET_UNUSABLE)
{
return addr;
}
/* Check if we've got to the end of this pred */
} else if ((bc_id == MB_BC_endof_pred) ||
(bc_id == MB_BC_enter_pred))
{
MB_SAY("Predicate does not contain "
"procedure: %s/%d mode %d",
pred_name,
(int) arity,
(int) mode_num);
return MB_CODE_INVALID_ADR;
}
} while (1);
return MB_CODE_INVALID_ADR;
}
MB_Word *
MB_code_data_alloc_words(MB_Word num_words)
{
code_data_count += num_words;
if (code_data_count >= MAX_CODE_DATA_COUNT) {
MB_fatal("Out of bytecode argument data space");
}
return code_arg_data + code_data_count - num_words;
}
/* given a code address, forces it into a valid range */
MB_Bytecode_Addr
MB_code_range_clamp(MB_Bytecode_Addr addr)
{
MB_Bytecode_Addr max_addr;
if ((MB_Unsigned) addr < (MB_Unsigned) code_id) return code_id;
max_addr = code_id + code_count - 1;
if ((MB_Unsigned) addr > (MB_Unsigned) max_addr) return max_addr;
return addr;
}
/*
** Returns true if a given instruction pointer points to a normal
** address (ie: valid range and not one of MB_CODE_xxxx)
*/
MB_Bool
MB_ip_normal(MB_Bytecode_Addr ip)
{
/* XXX pointer comparison; assume cast to unsigned will work */
return (((MB_Unsigned) ip >= (MB_Unsigned) code_id) &&
((MB_Unsigned) ip < (MB_Unsigned) (code_id + MAX_CODE_COUNT)));
}
/*
** Returns true if a given instruction pointer is a 'special'
** address (ie: one of the MB_CODE_xxxx macros)
*/
MB_Bool
MB_ip_special(MB_Bytecode_Addr ip)
{
return ((MB_Unsigned) ip > (MB_Unsigned) MB_CODE_INVALID_ADR);
}