mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-23 21:33:49 +00:00
Estimated hours taken: 8 Split up the startup interface so that there are seperate initialization and termination functions, rather than just a single mercury_runtime_main() function which does everything. Also change things so that the io__state data is stored in global variables. runtime/init.h: util/mkinit.c: runtime/wrapper.h: runtime/wrapper.mod: Move declarations for stuff defined in wrapper.mod from init.h to wrapper.h. Clean up the remainder of init.h so that it is clear which parts are interface and which are just there for use by *_init.c. Change the automatically-generated *_init.c files so that they call mercury_runtime_init(), mercury_runtime_main(), and mercury_runtime_terminate(), rather than just mercury_runtime_main(). Define these new two functions in wrapper.mod. Delete the library_entry_point; change do_interpreter to call program_entry_point directly, rather than via library_entry_point. runtime/engine.h: runtime/engine.mod: Add new function terminate_engine(). Delete the function start_mercury_engine(); move the code that used to be there to the end of init_engine(). runtime/context.h: runtime/context.mod: Add new function shutdown_processes(). (The current implementation is just a stub.) Add a call to debug_memory() in init_process_context(). runtime/memory.h: runtime/memory.c: Export the debug_memory() function, for use by context.c. library/io.m: library/io.nu.nl: Change things so that the io__state data is stored in C global variables (or, for Prolog, using assert/retract), rather than passing around a data structure. (Actually we still pass the data structure around, but it is just a dummy Word that never gets used.) Delete the old hand-coded io__run predicate, which was the library entry point; instead export C functions ML_io_init_state() and ML_io_finalize_state(). Move the code for handling profiling from io__run to do_interpreter in runtime/wrapper.mod. scripts/c2init.in: Change the default entry point from io__run_0_0 to main_2_0
425 lines
11 KiB
Modula-2
425 lines
11 KiB
Modula-2
/*
|
|
** Copyright (C) 1993-1997 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 "imp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <setjmp.h>
|
|
|
|
#include "engine.h"
|
|
|
|
#include "dummy.h"
|
|
|
|
#ifdef USE_GCC_NONLOCAL_GOTOS
|
|
|
|
#define LOCALS_SIZE 10024 /* amount of space to reserve for local vars */
|
|
#define MAGIC_MARKER 187 /* a random character */
|
|
#define MAGIC_MARKER_2 142 /* another random character */
|
|
|
|
#endif
|
|
|
|
static void call_engine_inner(Code *entry_point);
|
|
|
|
#ifndef USE_GCC_NONLOCAL_GOTOS
|
|
static Code *engine_done(void);
|
|
static Code *engine_init_registers(void);
|
|
#endif
|
|
|
|
bool debugflag[MAXFLAG];
|
|
|
|
static jmp_buf *engine_jmp_buf;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** init_engine() calls init_memory() which sets up all the necessary
|
|
** stuff for allocating memory-zones and other runtime areas (such as
|
|
** the zone structures and context structures). If PARALLEL is defined,
|
|
** this will cause the shared memory to be allocated.
|
|
** Next, init_engine() calls init_processes() which fork()s the right
|
|
** number of processes, and initializes the data structures for coordinating
|
|
** the interaction between multiple processes.
|
|
** Then, init_engine() calls init_process_context() which initializes the
|
|
** local context for this process including the heap and solutions heap.
|
|
** If it is the original process, it allocates the initial context for main.
|
|
**
|
|
** Finally, if there are multiple processes, init_engine calls
|
|
** call_engine(do_runnext) for all but the first one, which makes
|
|
** them sleep until work becomes available. The initial process
|
|
** returns to the caller.
|
|
*/
|
|
void
|
|
init_engine(void)
|
|
{
|
|
init_memory();
|
|
|
|
#ifndef USE_GCC_NONLOCAL_GOTOS
|
|
make_label("engine_done", LABEL(engine_done));
|
|
#endif
|
|
|
|
init_processes();
|
|
init_process_context();
|
|
|
|
if (my_procnum == 0) {
|
|
return;
|
|
} else {
|
|
call_engine(ENTRY(do_runnext));
|
|
/* not reached */
|
|
MR_assert(FALSE);
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** call_engine(Code *entry_point)
|
|
**
|
|
** This routine calls a Mercury routine from C.
|
|
**
|
|
** The called routine should be det/semidet/cc_multi/cc_nondet.
|
|
** The virtual machine registers must be set up correctly
|
|
** before the call. Specifically, the non-transient real registers
|
|
** must have valid values, and the fake_reg copies of the transient
|
|
** (register window) registers must have valid values; call_engine()
|
|
** will call restore_transient_registers() and will then assume that
|
|
** all the registers have been correctly set up.
|
|
**
|
|
** call_engine() will call save_registers() before returning.
|
|
** That will copy the real registers we use to the fake_reg array.
|
|
**
|
|
** Beware, however, that if you are planning to return to C code
|
|
** that did not #include "regs.h" (directly or via e.g. "imp.h"),
|
|
** and you have fiddled with the Mercury registers or invoked
|
|
** call_engine() or anything like that, then you will need to
|
|
** save the real registers that C is using before modifying the
|
|
** Mercury registers and then restore them afterwards.
|
|
**
|
|
** The called routine may invoke C functions; currently this
|
|
** is done by just invoking them directly, although that will
|
|
** have to change if we start using the caller-save registers.
|
|
**
|
|
** The called routine may invoke C functions which in turn
|
|
** invoke call_engine() to invoke invoke Mercury routines (which
|
|
** in turn invoke C functions which ... etc. ad infinitum.)
|
|
**
|
|
** call_engine() calls setjmp() and then invokes call_engine_inner()
|
|
** which does the real work. call_engine_inner() exits by calling
|
|
** longjmp() to return to call_engine(). There are two
|
|
** different implementations of call_engine_inner(), one for gcc,
|
|
** and another portable version that works on standard ANSI C compilers.
|
|
*/
|
|
|
|
void
|
|
call_engine(Code *entry_point)
|
|
{
|
|
|
|
jmp_buf curr_jmp_buf;
|
|
jmp_buf * volatile prev_jmp_buf;
|
|
|
|
/*
|
|
** Preserve the value of engine_jmp_buf on the C stack.
|
|
** This is so "C calls Mercury which calls C which calls Mercury" etc.
|
|
** will work.
|
|
*/
|
|
|
|
prev_jmp_buf = engine_jmp_buf;
|
|
engine_jmp_buf = &curr_jmp_buf;
|
|
|
|
/*
|
|
** Mark this as the spot to return to.
|
|
** On return, restore the registers (since longjmp may clobber
|
|
** them), restore the saved value of engine_jmp_buf, and then
|
|
** exit.
|
|
*/
|
|
|
|
if (setjmp(curr_jmp_buf)) {
|
|
debugmsg0("...caught longjmp\n");
|
|
restore_registers();
|
|
engine_jmp_buf = prev_jmp_buf;
|
|
return;
|
|
}
|
|
|
|
call_engine_inner(entry_point);
|
|
}
|
|
|
|
#ifdef USE_GCC_NONLOCAL_GOTOS
|
|
|
|
/* The gcc-specific version */
|
|
|
|
void
|
|
call_engine_inner(Code *entry_point)
|
|
{
|
|
/*
|
|
** Allocate some space for local variables in other
|
|
** procedures. This is done because we may jump into the middle
|
|
** of a C function, which may assume that space on the stack
|
|
** has already beened allocated for its variables. Such space
|
|
** would generally be used for expression temporary variables.
|
|
** How did we arrive at the correct value of LOCALS_SIZE?
|
|
** Good question. I think it's more voodoo than science.
|
|
**
|
|
** This used to be done by just calling
|
|
** alloca(LOCALS_SIZE), but on the mips that just decrements the
|
|
** stack pointer, whereas local variables are referenced
|
|
** via the frame pointer, so it didn't work.
|
|
** This technique should work and should be vaguely portable,
|
|
** just so long as local variables and temporaries are allocated in
|
|
** the same way in every function.
|
|
*/
|
|
|
|
unsigned char locals[LOCALS_SIZE];
|
|
{
|
|
|
|
#ifndef SPEED
|
|
{
|
|
/* ensure that we only make the label once */
|
|
static bool initialized = FALSE;
|
|
|
|
if (!initialized)
|
|
{
|
|
make_label("engine_done", LABEL(engine_done));
|
|
initialized = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** restore any registers that get clobbered by the C function
|
|
** call mechanism
|
|
*/
|
|
|
|
restore_transient_registers();
|
|
|
|
/*
|
|
** We save the address of the locals in a global pointer to make
|
|
** sure that gcc can't optimize them away.
|
|
*/
|
|
|
|
global_pointer = locals;
|
|
|
|
#ifndef SPEED
|
|
memset((void *)locals, MAGIC_MARKER, LOCALS_SIZE);
|
|
#endif
|
|
debugmsg1("in `call_engine', locals at %p\n", (void *)locals);
|
|
|
|
/*
|
|
** Now just call the entry point
|
|
*/
|
|
|
|
noprof_call(entry_point, LABEL(engine_done));
|
|
|
|
Define_label(engine_done);
|
|
/*
|
|
** We need to ensure that there is at least one
|
|
** real function call in call_engine(), because
|
|
** otherwise gcc thinks that it doesn't need to
|
|
** restore the caller-save registers (such as
|
|
** the return address!) because it thinks call_engine() is
|
|
** a leaf routine which doesn't call anything else,
|
|
** and so it thinks that they won't have been clobbered.
|
|
**
|
|
** This probably isn't necessary now that we exit from this function
|
|
** using longjmp(), but it doesn't do much harm, so I'm leaving it in.
|
|
*/
|
|
|
|
dummy_function_call();
|
|
|
|
debugmsg1("in label `engine_done', locals at %p\n", locals);
|
|
|
|
#ifndef SPEED
|
|
/*
|
|
** Check how much of the space we reserved for local variables
|
|
** was actually used.
|
|
*/
|
|
|
|
if (check_space) {
|
|
int low = 0, high = LOCALS_SIZE;
|
|
int used_low, used_high;
|
|
|
|
while (low < high && locals[low] == MAGIC_MARKER) {
|
|
low++;
|
|
}
|
|
while (low < high && locals[high - 1] == MAGIC_MARKER) {
|
|
high--;
|
|
}
|
|
used_low = high;
|
|
used_high = LOCALS_SIZE - low;
|
|
printf("max locals used: %3d bytes (probably)\n",
|
|
min(high, LOCALS_SIZE - low));
|
|
printf("(low mark = %d, high mark = %d)\n", low, high);
|
|
}
|
|
#endif /* not SPEED */
|
|
|
|
/*
|
|
** Despite the above precautions with allocating a large chunk
|
|
** of unused stack space, the return address may still have been
|
|
** stored on the top of the stack, past our dummy locals,
|
|
** where it may have been clobbered.
|
|
** Hence the only safe way to exit is with longjmp().
|
|
**
|
|
** Since longjmp() may clobber the registers, we need to
|
|
** save them first.
|
|
*/
|
|
save_registers();
|
|
debugmsg0("longjmping out...\n");
|
|
longjmp(*engine_jmp_buf, 1);
|
|
}} /* end call_engine_inner() */
|
|
|
|
/* with nonlocal gotos, we don't save the previous locations */
|
|
void
|
|
dump_prev_locations(void) {}
|
|
|
|
#else /* not USE_GCC_NONLOCAL_GOTOS */
|
|
|
|
/*
|
|
** The portable version
|
|
**
|
|
** To keep the main dispatch loop tight, instead of returning a null
|
|
** pointer to indicate when we've finished executing, we just longjmp()
|
|
** out. We need to save the registers before calling longjmp(),
|
|
** since doing a longjmp() might clobber them.
|
|
**
|
|
** With register windows, we need to restore the registers to
|
|
** their initialized values from their saved copies.
|
|
** This must be done in a function engine_init_registers() rather
|
|
** than directly from call_engine_inner() because otherwise their value
|
|
** would get mucked up because of the function call from call_engine_inner().
|
|
*/
|
|
|
|
static Code *
|
|
engine_done(void)
|
|
{
|
|
save_registers();
|
|
debugmsg0("longjmping out...\n");
|
|
longjmp(*engine_jmp_buf, 1);
|
|
}
|
|
|
|
static Code *
|
|
engine_init_registers(void)
|
|
{
|
|
restore_transient_registers();
|
|
succip = engine_done;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** For debugging purposes, we keep a circular buffer of
|
|
** the last 40 locations that we jumped to. This is
|
|
** very useful for determining the cause of a crash,
|
|
** since it runs a lot faster than -dg.
|
|
*/
|
|
|
|
#define NUM_PREV_FPS 40
|
|
|
|
typedef void (*FuncPtr)(void);
|
|
typedef Code *Func(void);
|
|
|
|
static FuncPtr prev_fps[NUM_PREV_FPS];
|
|
static int prev_fp_index = 0;
|
|
|
|
void
|
|
dump_prev_locations(void)
|
|
{
|
|
int i, pos;
|
|
|
|
#if defined(SPEED) && !defined(DEBUG_GOTOS)
|
|
if (tracedebug)
|
|
#endif
|
|
{
|
|
printf("previous %d locations:\n", NUM_PREV_FPS);
|
|
for (i = 0; i < NUM_PREV_FPS; i++) {
|
|
pos = (i + prev_fp_index) % NUM_PREV_FPS;
|
|
printlabel(prev_fps[pos]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
call_engine_inner(Code *entry_point)
|
|
{
|
|
reg Func *fp;
|
|
|
|
/*
|
|
** Start up the actual engine.
|
|
** The loop is unrolled a bit for efficiency.
|
|
*/
|
|
|
|
fp = engine_init_registers;
|
|
fp = (*fp)();
|
|
fp = entry_point;
|
|
|
|
#if defined(SPEED) && !defined(DEBUG_GOTOS)
|
|
if (!tracedebug) {
|
|
for (;;)
|
|
{
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
fp = (*fp)();
|
|
}
|
|
} else
|
|
#endif
|
|
for (;;)
|
|
{
|
|
prev_fps[prev_fp_index] = (FuncPtr) fp;
|
|
|
|
if (++prev_fp_index >= NUM_PREV_FPS)
|
|
prev_fp_index = 0;
|
|
|
|
debuggoto(fp);
|
|
debugsreg();
|
|
fp = (*fp)();
|
|
}
|
|
} /* end call_engine_inner() */
|
|
#endif /* not USE_GCC_NONLOCAL_GOTOS */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
void
|
|
terminate_engine(void)
|
|
{
|
|
/*
|
|
** we don't bother to deallocate memory...
|
|
** that will happen automatically on process exit anyway.
|
|
*/
|
|
|
|
shutdown_processes();
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
BEGIN_MODULE(special_labels_module)
|
|
|
|
BEGIN_CODE
|
|
|
|
do_redo:
|
|
redo();
|
|
|
|
do_fail:
|
|
fail();
|
|
|
|
do_succeed:
|
|
succeed();
|
|
|
|
do_last_succeed:
|
|
succeed_discard();
|
|
|
|
do_not_reached:
|
|
printf("reached not_reached\n");
|
|
exit(1);
|
|
#ifndef USE_GCC_NONLOCAL_GOTOS
|
|
return 0;
|
|
#endif
|
|
|
|
END_MODULE
|
|
|
|
/*---------------------------------------------------------------------------*/
|