Files
mercury/runtime/mercury_wrapper.c
Zoltan Somogyi af6f18ab3f Fergus's recent change to the handling of some builtins broke the tracing
Estimated hours taken: 20

Fergus's recent change to the handling of some builtins broke the tracing
of those builtins. The following changes are a fix for this.

compiler/polymorphism.m:
	Export the predicate that checks whether a predicate is a builtin
	that lacks the usually necessary typeinfos.

	Comment out a misleading and in any case not very useful progress
	message.

compiler/liveness.m:
	Turn off type_info liveness for builtins without typeinfos.
	Since these builtins establish no gc points and shouldn't be
	execution traced, this is OK.

	Make type_info liveness part of live_info, since it can now be
	incorrect to look up the value of the option. (This may yield
	a speedup.)

compiler/live_vars.m:
compiler/store_alloc.m:
	Pass the pred_id to initial_liveness to liveness.m can do the test.

compiler/passes_aux.m:
	Add a new traversal type that passes along the pred_id.

compiler/mercury_compile.m:
	Turn off execution tracing for the modules builtin.m and
	private_builtin.m. The latter contains the interface predicates
	for the builtins without typeinfos. Since the interface predicates
	also lack the typeinfos, the compiler would get an internal abort
	if we left execution tracing on.

	In any case, these two modules contain stuff that users should
	consider language builtins, which means they should not be execution
	traced (they can still be stack traced in the right grade).

	Use the new traversal type for the modules that now need the pred_id.

compiler/globals.m:
	Allow the trace level to be set from outside, in this case
	mercury_compile.m.

The next batch of changes have to do with adding a stack dump command
to the debugger. Since debugging is possible even in non-debug grades,
this in turn requires allowing stack tracing to work in non-debug grades,
on programs in which only some modules are compiled with execution
(and hence stack) tracing.

compiler/llds_out.m:
compiler/mercury_compile.m:
runtime/mercury_conf_param.h:
	Llds_out used to output "#include <mercury_imp.h>" as the first
	substantive thing in the generated C file. The set of #define
	parameters in effect when mercury_imp.h is processed determines
	whether the macros that optionally register stack layouts for label
	actually do so or not. The values of these parameters are derived
	from the grade, which means that with this setup it is not possible
	for a non-debug grade program to register its stack layouts in the
	label table.

	The new version of llds_out looks up the option that says whether
	this module is compiled with execution tracing or not, and if it is,
	it generates a #define MR_STACK_TRACE_THIS_MODULE *before* the #include
	of mercury_imp.h. This causes mercury_conf_param.h, included from
	mercury_imp.h, to define the macros MR_USE_STACK_LAYOUTS and
	and MR_INSERT_LABELS, which in turn cause stack layouts for labels
	in this module to be generated and to be inserted into the label
	table, *without* changing the grade string (this last part is why
	we do not simply define MR_STACK_TRACE).

	Use the same mechanism to #include mercury_trace.h when doing
	execution tracing, since it is simpler than the mechanism we
	used to use (mercury_compile.m including the #include in a list
	of C header file fragments).

compiler/mercury_compile.m:
runtime/mercury_conf_param.h:
	Split the MR_NEED_INITIALIZATION_CODE macro into two parts.
	The first, MR_MAY_NEED_INITIALIZATION, now controls whether
	initialization code makes it into the object file of a module.
	The second, MR_NEED_INITIALIZATION_AT_START, determines whether
	the initialization code is called before main/2.

	When a module is compiled with execution tracing, the macro
	MR_INSERT_LABELS turns on MR_MAY_NEED_INITIALIZATION but not
	MR_NEED_INITIALIZATION_AT_START. The debugger will make sure
	that the initialization code has been called before it tries
	to do a stack dump (which needs the initialization code to have
	been executed, because it needs labels to have been put into the label
	table so that from a return address it can find the layout of the
	proc to which it belongs).

	Define MR_NEED_INITIALIZATION_AT_START if PROFILE_TIME is defined,
	since if PROFILE_TIME is defined mercury_wrapper.c calls init_modules.
	The fact that MR_NEED_INITIALIZATION_CODE didn't used to be defined
	when PROFILE_TIME was defined was, I believe, a bug, which was
	not detected because we do not turn on PROFILE_TIME without also
	turning on PROFILE_CALLS.

runtime/mercury_stack_trace.[ch]:
	Change the way stack dumps are done, to make it possible to
	print stack dumps from the debugger and to use trivial run-length
	encoding on the output (so that 100 consecutive calls to p
	yield the line "p * 100", rather than 100 lines of "p").

	The stack routine now returns an indication of whether the stack dump
	was fully successful, and if not, a description of the reason why not.
	This requires knowing when we have found the end of the stack dump,
	so we provide a global variable, MR_stack_trace_bottom, which
	mercury_wrapper.c will set to global_success, the address main/2
	goes to on success.

	s/multidet/multi/

runtime/mercury_wrapper.c:
	Set MR_stack_trace_bottom to the address of globals_success.
	Use MR_NEED_INITIALIZATION_AT_START to decide whether to call
	do_init_modules.

runtime/mercury_stacks.h:
	Provide variants of detstackvar(n) and framevar(n) that look up sp and
	curfr in an array of saved regs, for use by the debugger.

runtime/mercury_trace_util.c:
	Use the new variants of detstackvar(n) and framevar(n). This fixes
	an old bug on SPARCs.

runtime/mercury_trace_internal.c:
	Completely reorganize the way debugger commands are handled.
	Centralize reading in command lines, and the breaking up of command
	lines into words. The command names are the same as they were,
	but command syntax is now much easier to change.

	Add a new command "d" to dump as much of the stack as the available
	information will allow.

runtime/mercury_goto.h:
	Cosmetic changes to avoid the use of two different conditional
	compilation layout styles.

util/mkinit.c:
	Since we cannot know when we generate the _init.c file whether any
	modules will be compiled with execution tracing and will thus need
	stack tracing, we must now include in the generated _init.c file the
	code to call the initialization functions in all the modules, even if
	MR_NEED_INITIALIZATION_AT_START is not set, since init_modules
	can be called later, from the debugger. (We should be able to
	use the same approach with the accurate collector.)
1998-06-08 08:27:15 +00:00

1017 lines
24 KiB
C

/*
INIT mercury_sys_init_wrapper
ENDINIT
*/
/*
** Copyright (C) 1994-1998 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.
*/
/*
** file: mercury_wrapper.c
** main authors: zs, fjh
**
** This file contains the startup and termination entry points
** for the Mercury runtime.
**
** It defines mercury_runtime_init(), which is invoked from
** mercury_init() in the C file generated by util/mkinit.c.
** The code for mercury_runtime_init() initializes various things, and
** processes options (which are specified via an environment variable).
**
** It also defines mercury_runtime_main(), which invokes
** call_engine(do_interpreter), which invokes main/2.
**
** It also defines mercury_runtime_terminate(), which performs
** various cleanups that are needed to terminate cleanly.
*/
#include "mercury_imp.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "mercury_timing.h"
#include "mercury_getopt.h"
#include "mercury_init.h"
#include "mercury_dummy.h"
#include "mercury_trace.h"
/* global variables concerned with testing (i.e. not with the engine) */
/* command-line options */
/* size of data areas (including redzones), in kilobytes */
/* (but we later multiply by 1024 to convert to bytes) */
size_t heap_size = 4096;
size_t detstack_size = 2048;
size_t nondstack_size = 128;
size_t solutions_heap_size = 1024;
size_t trail_size = 128;
/* size of the redzones at the end of data areas, in kilobytes */
/* (but we later multiply by 1024 to convert to bytes) */
size_t heap_zone_size = 16;
size_t detstack_zone_size = 16;
size_t nondstack_zone_size = 16;
size_t solutions_heap_zone_size = 16;
size_t trail_zone_size = 16;
/* primary cache size to optimize for, in kilobytes */
/* (but we later multiply by 1024 to convert to bytes) */
size_t pcache_size = 8192;
/* other options */
bool check_space = FALSE;
static bool benchmark_all_solns = FALSE;
static bool use_own_timer = FALSE;
static int repeats = 1;
/* timing */
int time_at_last_stat;
int time_at_start;
static int time_at_finish;
/* time profiling */
enum MR_TimeProfileMethod
MR_time_profile_method = MR_profile_user_plus_system_time;
const char * progname;
int mercury_argc; /* not counting progname */
char ** mercury_argv;
int mercury_exit_status = 0;
bool MR_profiling = TRUE;
/*
** EXTERNAL DEPENDENCIES
**
** - The Mercury runtime initialization, namely mercury_runtime_init(),
** calls the functions init_gc() and init_modules(), which are in
** the automatically generated C init file; mercury_init_io(), which is
** in the Mercury library; and it calls the predicate io__init_state/2
** in the Mercury library.
** - The Mercury runtime main, namely mercury_runtime_main(),
** calls main/2 in the user's program.
** - The Mercury runtime finalization, namely mercury_runtime_terminate(),
** calls io__finalize_state/2 in the Mercury library.
**
** But, to enable Quickstart of shared libraries on Irix 5,
** and in general to avoid various other complications
** with shared libraries and/or Windows DLLs,
** we need to make sure that we don't have any undefined
** external references when building the shared libraries.
** Hence the statically linked init file saves the addresses of those
** procedures in the following global variables.
** This ensures that there are no cyclic dependencies;
** the order is user program -> library -> runtime -> gc,
** where `->' means "depends on", i.e. "references a symbol of".
*/
void (*address_of_mercury_init_io)(void);
void (*address_of_init_modules)(void);
#ifdef CONSERVATIVE_GC
void (*address_of_init_gc)(void);
#endif
Code *program_entry_point;
/* normally mercury__main_2_0 (main/2) */
void (*MR_library_initializer)(void);
/* normally ML_io_init_state (io__init_state/2)*/
void (*MR_library_finalizer)(void);
/* normally ML_io_finalize_state (io__finalize_state/2) */
Code *MR_library_trace_browser;
/* normally mercury__io__print_3_0 (io__print/3) */
void (*MR_DI_output_current_ptr)(Integer, Integer, Integer, Word, String,
String, Integer, Integer, Integer, Word, String, Word, Word);
/* normally ML_DI_output_current (output_current/13) */
bool (*MR_DI_found_match)(Integer, Integer, Integer, Word, String, String,
Integer, Integer, Integer, Word, String, Word);
/* normally ML_DI_found_match (output_current/12) */
void (*MR_DI_read_request_from_socket)(Word, Word *, Integer *);
#ifdef USE_GCC_NONLOCAL_GOTOS
#define SAFETY_BUFFER_SIZE 1024 /* size of stack safety buffer */
#define MAGIC_MARKER_2 142 /* a random character */
#endif
static void process_args(int argc, char **argv);
static void process_environment_options(void);
static void process_options(int argc, char **argv);
static void usage(void);
static void make_argv(const char *, char **, char ***, int *);
#ifdef MEASURE_REGISTER_USAGE
static void print_register_usage_counts(void);
#endif
Declare_entry(do_interpreter);
/*---------------------------------------------------------------------------*/
void
mercury_runtime_init(int argc, char **argv)
{
bool saved_trace_enabled;
#if NUM_REAL_REGS > 0
Word c_regs[NUM_REAL_REGS];
#endif
/*
** Save the callee-save registers; we're going to start using them
** as global registers variables now, which will clobber them,
** and we need to preserve them, because they're callee-save,
** and our caller may need them ;-)
*/
save_regs_to_mem(c_regs);
#ifdef MR_LOWLEVEL_DEBUG
/*
** Ensure stdio & stderr are unbuffered even if redirected.
** Using setvbuf() is more complicated than using setlinebuf(),
** but also more portable.
*/
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
#endif
#ifdef CONSERVATIVE_GC
GC_quiet = TRUE;
/*
** Call GC_INIT() to tell the garbage collector about this DLL.
** (This is necessary to support Windows DLLs using gnu-win32.)
*/
GC_INIT();
/*
** call the init_gc() function defined in <foo>_init.c,
** which calls GC_INIT() to tell the GC about the main program.
** (This is to work around a Solaris 2.X (X <= 4) linker bug,
** and also to support Windows DLLs using gnu-win32.)
*/
(*address_of_init_gc)();
/*
** Double-check that the garbage collector knows about
** global variables in shared libraries.
*/
GC_is_visible(fake_reg);
/* The following code is necessary to tell the conservative */
/* garbage collector that we are using tagged pointers */
{
int i;
for (i = 1; i < (1 << TAGBITS); i++) {
GC_register_displacement(i);
}
}
#endif
/*
** Process the command line and the options in the environment
** variable MERCURY_OPTIONS, and save results in global variables.
*/
process_args(argc, argv);
process_environment_options();
/*
** Some of the rest of this function may call Mercury code
** that may have been compiled with tracing (e.g. the initialization
** routines in the library called via MR_library_initializer).
** Since this initialization code shouldn't be traced, we disable
** tracing until the end of this function.
*/
saved_trace_enabled = MR_trace_enabled;
MR_trace_enabled = FALSE;
#ifdef MR_NEED_INITIALIZATION_AT_START
do_init_modules();
#endif
(*address_of_mercury_init_io)();
/* start up the Mercury engine */
init_engine();
/* initialize profiling */
if (MR_profiling) MR_prof_init();
/*
** We need to call save_registers(), since we're about to
** call a C->Mercury interface function, and the C->Mercury
** interface convention expects them to be saved. And before we
** can do that, we need to call restore_transient_registers(),
** since we've just returned from a C call.
*/
restore_transient_registers();
save_registers();
MR_trace_init();
/* initialize the Mercury library */
(*MR_library_initializer)();
/*
** Now the real tracing starts; undo any updates to the trace state
** made by the trace code in the library initializer.
*/
MR_trace_start(saved_trace_enabled);
/*
** Restore the callee-save registers before returning,
** since they may be used by the C code that called us.
*/
restore_regs_from_mem(c_regs);
} /* end runtime_mercury_main() */
void
do_init_modules(void)
{
static bool done = FALSE;
if (! done) {
(*address_of_init_modules)();
done = TRUE;
}
}
/*
** Given a string, parse it into arguments and create an argv vector for it.
** Returns args, argv, and argc. It is the caller's responsibility to oldmem()
** args and argv when they are no longer needed.
*/
static void
make_argv(const char *string, char **args_ptr, char ***argv_ptr, int *argc_ptr)
{
char *args;
char **argv;
const char *s = string;
char *d;
int args_len = 0;
int argc = 0;
int i;
/*
** First do a pass over the string to count how much space we need to
** allocate
*/
for (;;) {
/* skip leading whitespace */
while(isspace((unsigned char)*s)) {
s++;
}
/* are there any more args? */
if(*s != '\0') {
argc++;
} else {
break;
}
/* copy arg, translating backslash escapes */
if (*s == '"') {
s++;
/* "double quoted" arg - scan until next double quote */
while (*s != '"') {
if (s == '\0') {
fatal_error(
"Mercury runtime: unterminated quoted string\n"
"in MERCURY_OPTIONS environment variable\n"
);
}
if (*s == '\\')
s++;
args_len++; s++;
}
s++;
} else {
/* ordinary white-space delimited arg */
while(*s != '\0' && !isspace((unsigned char)*s)) {
if (*s == '\\')
s++;
args_len++; s++;
}
}
args_len++;
} /* end for */
/*
** Allocate the space
*/
args = make_many(char, args_len);
argv = make_many(char *, argc + 1);
/*
** Now do a pass over the string, copying the arguments into `args'
** setting up the contents of `argv' to point to the arguments.
*/
s = string;
d = args;
for(i = 0; i < argc; i++) {
/* skip leading whitespace */
while(isspace((unsigned char)*s)) {
s++;
}
/* are there any more args? */
if(*s != '\0') {
argv[i] = d;
} else {
argv[i] = NULL;
break;
}
/* copy arg, translating backslash escapes */
if (*s == '"') {
s++;
/* "double quoted" arg - scan until next double quote */
while (*s != '"') {
if (*s == '\\')
s++;
*d++ = *s++;
}
s++;
} else {
/* ordinary white-space delimited arg */
while(*s != '\0' && !isspace((unsigned char)*s)) {
if (*s == '\\')
s++;
*d++ = *s++;
}
}
*d++ = '\0';
} /* end for */
*args_ptr = args;
*argv_ptr = argv;
*argc_ptr = argc;
} /* end make_argv() */
/**
** process_args() is a function that sets some global variables from the
** command line. `mercury_arg[cv]' are `arg[cv]' without the program name.
** `progname' is program name.
**/
static void
process_args( int argc, char ** argv)
{
progname = argv[0];
mercury_argc = argc - 1;
mercury_argv = argv + 1;
}
/**
** process_environment_options() is a function to parse the MERCURY_OPTIONS
** environment variable.
**/
static void
process_environment_options(void)
{
char* options;
options = getenv("MERCURY_OPTIONS");
if (options != NULL) {
char *arg_str, **argv;
char *dummy_command_line;
int argc;
int c;
/*
getopt() expects the options to start in argv[1],
not argv[0], so we need to insert a dummy program
name (we use "x") at the start of the options before
passing them to make_argv() and then to getopt().
*/
dummy_command_line = make_many(char, strlen(options) + 3);
strcpy(dummy_command_line, "x ");
strcat(dummy_command_line, options);
make_argv(dummy_command_line, &arg_str, &argv, &argc);
oldmem(dummy_command_line);
process_options(argc, argv);
oldmem(arg_str);
oldmem(argv);
}
}
static void
process_options(int argc, char **argv)
{
unsigned long size;
int c;
while ((c = getopt(argc, argv, "acC:d:D:hLlP:pr:s:tT:w:xz:")) != EOF)
{
switch (c)
{
case 'a':
benchmark_all_solns = TRUE;
break;
case 'c':
check_space = TRUE;
break;
case 'C':
if (sscanf(optarg, "%lu", &size) != 1)
usage();
pcache_size = size * 1024;
break;
case 'd':
if (streq(optarg, "b"))
nondstackdebug = TRUE;
else if (streq(optarg, "c"))
calldebug = TRUE;
else if (streq(optarg, "d"))
detaildebug = TRUE;
else if (streq(optarg, "g"))
gotodebug = TRUE;
else if (streq(optarg, "G"))
#ifdef CONSERVATIVE_GC
GC_quiet = FALSE;
#else
fatal_error("-dG: GC not enabled");
#endif
else if (streq(optarg, "s"))
detstackdebug = TRUE;
else if (streq(optarg, "h"))
heapdebug = TRUE;
else if (streq(optarg, "f"))
finaldebug = TRUE;
else if (streq(optarg, "p"))
progdebug = TRUE;
else if (streq(optarg, "m"))
memdebug = TRUE;
else if (streq(optarg, "r"))
sregdebug = TRUE;
else if (streq(optarg, "t"))
tracedebug = TRUE;
else if (streq(optarg, "a")) {
calldebug = TRUE;
nondstackdebug = TRUE;
detstackdebug = TRUE;
heapdebug = TRUE;
gotodebug = TRUE;
sregdebug = TRUE;
finaldebug = TRUE;
tracedebug = TRUE;
#ifdef CONSERVATIVE_GC
GC_quiet = FALSE;
#endif
}
else
usage();
use_own_timer = FALSE;
break;
case 'D':
MR_trace_enabled = TRUE;
if (streq(optarg, "i"))
MR_trace_handler = MR_TRACE_INTERNAL;
#ifdef MR_USE_EXTERNAL_DEBUGGER
else if (streq(optarg, "e"))
MR_trace_handler = MR_TRACE_EXTERNAL;
#endif
else
usage();
break;
case 'h':
usage();
break;
case 'L':
do_init_modules();
break;
case 'l': {
List *ptr;
List *label_list;
label_list = get_all_labels();
for_list (ptr, label_list) {
Label *label;
label = (Label *) ldata(ptr);
printf("%lu %lx %s\n",
(unsigned long) label->e_addr,
(unsigned long) label->e_addr,
label->e_name);
}
exit(0);
}
case 'p':
MR_profiling = FALSE;
break;
#ifdef PARALLEL
case 'P':
if (sscanf(optarg, "%u", &numprocs) != 1)
usage();
if (numprocs < 1)
usage();
break;
#endif
case 'r':
if (sscanf(optarg, "%d", &repeats) != 1)
usage();
break;
case 's':
if (sscanf(optarg+1, "%lu", &size) != 1)
usage();
if (optarg[0] == 'h')
heap_size = size;
else if (optarg[0] == 'd')
detstack_size = size;
else if (optarg[0] == 'n')
nondstack_size = size;
else if (optarg[0] == 'l')
entry_table_size = size *
1024 / (2 * sizeof(List *));
#ifdef MR_USE_TRAIL
else if (optarg[0] == 't')
trail_size = size;
#endif
else
usage();
break;
case 't':
use_own_timer = TRUE;
calldebug = FALSE;
nondstackdebug = FALSE;
detstackdebug = FALSE;
heapdebug = FALSE;
gotodebug = FALSE;
sregdebug = FALSE;
finaldebug = FALSE;
break;
case 'T':
if (streq(optarg, "r")) {
MR_time_profile_method = MR_profile_real_time;
} else if (streq(optarg, "v")) {
MR_time_profile_method = MR_profile_user_time;
} else if (streq(optarg, "p")) {
MR_time_profile_method =
MR_profile_user_plus_system_time;
} else {
usage();
}
break;
case 'w': {
Label *which_label;
which_label = lookup_label_name(optarg);
if (which_label == NULL)
{
fprintf(stderr, "Mercury runtime: "
"label name `%s' unknown\n",
optarg);
exit(1);
}
program_entry_point = which_label->e_addr;
break;
}
case 'x':
#ifdef CONSERVATIVE_GC
GC_dont_gc = TRUE;
#endif
break;
case 'z':
if (sscanf(optarg+1, "%lu", &size) != 1)
usage();
if (optarg[0] == 'h')
heap_zone_size = size;
else if (optarg[0] == 'd')
detstack_zone_size = size;
else if (optarg[0] == 'n')
nondstack_zone_size = size;
#ifdef MR_USE_TRAIL
else if (optarg[0] == 't')
trail_zone_size = size;
#endif
else
usage();
break;
default:
usage();
} /* end switch */
} /* end while */
} /* end process_options() */
static void
usage(void)
{
printf("Mercury runtime usage:\n"
"MERCURY_OPTIONS=\"[-hclLtxp] [-T[rvp]] [-d[abcdghs]]\n"
" [-[szt][hdn]#] [-C#] [-r#] [-w name] [-[123]#]\"\n"
"-h \t\tprint this usage message\n"
"-c \t\tcheck cross-function stack usage\n"
"-l \t\tprint all labels\n"
"-L \t\tcheck for duplicate labels\n"
"-t \t\ttime program execution\n"
"-x \t\tdisable garbage collection\n"
"-p \t\tdisable profiling\n"
"-Tr \t\tprofile real time (using ITIMER_REAL)\n"
"-Tv \t\tprofile user time (using ITIMER_VIRTUAL)\n"
"-Tp \t\tprofile user + system time (using ITIMER_PROF)\n"
"-dg \t\tdebug gotos\n"
"-dc \t\tdebug calls\n"
"-db \t\tdebug backtracking\n"
"-dh \t\tdebug heap\n"
"-ds \t\tdebug detstack\n"
"-df \t\tdebug final success/failure\n"
"-da \t\tdebug all\n"
"-dm \t\tdebug memory allocation\n"
"-dG \t\tdebug garbage collection\n"
"-dd \t\tdetailed debug\n"
"-Di \t\tdebug the program using the internal debugger\n"
#ifdef MR_USE_EXTERNAL_DEBUGGER
"-De \t\tdebug the program using the external debugger\n"
#endif
"-sh<n> \t\tallocate n kb for the heap\n"
"-sd<n> \t\tallocate n kb for the det stack\n"
"-sn<n> \t\tallocate n kb for the nondet stack\n"
#ifdef MR_USE_TRAIL
"-st<n> \t\tallocate n kb for the trail\n"
#endif
"-sl<n> \t\tallocate n kb for the label table\n"
"-zh<n> \t\tallocate n kb for the heap redzone\n"
"-zd<n> \t\tallocate n kb for the det stack redzone\n"
"-zn<n> \t\tallocate n kb for the nondet stack redzone\n"
#ifdef MR_USE_TRAIL
"-zt<n> \t\tallocate n kb for the trail redzone\n"
#endif
"-C<n> \t\tprimary cache size in kbytes\n"
#ifdef PARALLEL
"-P<n> \t\tnumber of processes to use for parallel execution\n"
"\t\tapplies only if Mercury is configured with --enable-parallel\n"
#endif
"-r<n> \t\trepeat n times\n"
"-w<name> \tcall predicate with given name (default: main/2)\n"
);
fflush(stdout);
exit(1);
} /* end usage() */
/*---------------------------------------------------------------------------*/
void
mercury_runtime_main(void)
{
#if NUM_REAL_REGS > 0
Word c_regs[NUM_REAL_REGS];
#endif
#if defined(MR_LOWLEVEL_DEBUG) && defined(USE_GCC_NONLOCAL_GOTOS)
unsigned char safety_buffer[SAFETY_BUFFER_SIZE];
#endif
static int repcounter;
/*
** Save the C callee-save registers
** and restore the Mercury registers
*/
save_regs_to_mem(c_regs);
restore_registers();
#if defined(MR_LOWLEVEL_DEBUG) && defined(USE_GCC_NONLOCAL_GOTOS)
/*
** double-check to make sure that we're not corrupting
** the C stack with these non-local gotos, by filling
** a buffer with a known value and then later checking
** that it still contains only this value
*/
global_pointer_2 = safety_buffer; /* defeat optimization */
memset(safety_buffer, MAGIC_MARKER_2, SAFETY_BUFFER_SIZE);
#endif
#ifdef MR_LOWLEVEL_DEBUG
#ifndef CONSERVATIVE_GC
heap_zone->max = heap_zone->min;
#endif
detstack_zone->max = detstack_zone->min;
nondetstack_zone->max = nondetstack_zone->min;
#endif
time_at_start = MR_get_user_cpu_miliseconds();
time_at_last_stat = time_at_start;
for (repcounter = 0; repcounter < repeats; repcounter++) {
debugmsg0("About to call engine\n");
call_engine(ENTRY(do_interpreter));
debugmsg0("Returning from call_engine()\n");
}
if (use_own_timer) {
time_at_finish = MR_get_user_cpu_miliseconds();
}
#if defined(USE_GCC_NONLOCAL_GOTOS) && defined(MR_LOWLEVEL_DEBUG)
{
int i;
for (i = 0; i < SAFETY_BUFFER_SIZE; i++)
MR_assert(safety_buffer[i] == MAGIC_MARKER_2);
}
#endif
if (detaildebug) {
debugregs("after final call");
}
#ifdef MR_LOWLEVEL_DEBUG
if (memdebug) {
printf("\n");
#ifndef CONSERVATIVE_GC
printf("max heap used: %6ld words\n",
(long) (heap_zone->max - heap_zone->min));
#endif
printf("max detstack used: %6ld words\n",
(long)(detstack_zone->max - detstack_zone->min));
printf("max nondstack used: %6ld words\n",
(long) (nondetstack_zone->max - nondetstack_zone->min));
}
#endif
#ifdef MEASURE_REGISTER_USAGE
printf("\n");
print_register_usage_counts();
#endif
if (use_own_timer) {
printf("%8.3fu ",
((double) (time_at_finish - time_at_start)) / 1000);
}
/*
** Save the Mercury registers and
** restore the C callee-save registers before returning,
** since they may be used by the C code that called us.
*/
save_registers();
restore_regs_from_mem(c_regs);
} /* end mercury_runtime_main() */
#ifdef MEASURE_REGISTER_USAGE
static void
print_register_usage_counts(void)
{
int i;
printf("register usage counts:\n");
for (i = 0; i < MAX_RN; i++) {
if (1 <= i && i <= ORD_RN) {
printf("r%d", i);
} else {
switch (i) {
case SI_RN:
printf("succip");
break;
case HP_RN:
printf("hp");
break;
case SP_RN:
printf("sp");
break;
case CF_RN:
printf("curfr");
break;
case MF_RN:
printf("maxfr");
break;
case MR_TRAIL_PTR_RN:
printf("MR_trail_ptr");
break;
case MR_TICKET_COUNTER_RN:
printf("MR_ticket_counter");
break;
default:
printf("UNKNOWN%d", i);
break;
}
}
printf("\t%lu\n", num_uses[i]);
} /* end for */
} /* end print_register_usage_counts() */
#endif
Define_extern_entry(do_interpreter);
Declare_label(global_success);
Declare_label(global_fail);
Declare_label(all_done);
MR_MAKE_STACK_LAYOUT_ENTRY(do_interpreter);
MR_MAKE_STACK_LAYOUT_INTERNAL_WITH_ENTRY(global_success, do_interpreter);
MR_MAKE_STACK_LAYOUT_INTERNAL_WITH_ENTRY(global_fail, do_interpreter);
MR_MAKE_STACK_LAYOUT_INTERNAL_WITH_ENTRY(all_done, do_interpreter);
BEGIN_MODULE(interpreter_module)
init_entry(do_interpreter);
init_label_sl(global_success);
init_label_sl(global_fail);
init_label_sl(all_done);
BEGIN_CODE
Define_entry(do_interpreter);
push(MR_hp);
push(MR_succip);
push(MR_maxfr);
mkframe("interpreter", 1, LABEL(global_fail));
if (program_entry_point == NULL) {
fatal_error("no program entry point supplied");
}
MR_stack_trace_bottom = LABEL(global_success);
#ifdef PROFILE_TIME
if (MR_profiling) MR_prof_turn_on_time_profiling();
#endif
noprof_call(program_entry_point, LABEL(global_success));
Define_label(global_success);
#ifdef MR_LOWLEVEL_DEBUG
if (finaldebug) {
save_transient_registers();
printregs("global succeeded");
if (detaildebug)
dumpnondstack();
}
#endif
if (benchmark_all_solns)
redo();
else
GOTO_LABEL(all_done);
Define_label(global_fail);
#ifdef MR_LOWLEVEL_DEBUG
if (finaldebug) {
save_transient_registers();
printregs("global failed");
if (detaildebug)
dumpnondstack();
}
#endif
Define_label(all_done);
#ifdef PROFILE_TIME
if (MR_profiling) MR_prof_turn_off_time_profiling();
#endif
MR_maxfr = (Word *) pop();
MR_succip = (Code *) pop();
MR_hp = (Word *) pop();
#ifdef MR_LOWLEVEL_DEBUG
if (finaldebug && detaildebug) {
save_transient_registers();
printregs("after popping...");
}
#endif
proceed();
#ifndef USE_GCC_NONLOCAL_GOTOS
return 0;
#endif
END_MODULE
/*---------------------------------------------------------------------------*/
int
mercury_runtime_terminate(void)
{
#if NUM_REAL_REGS > 0
Word c_regs[NUM_REAL_REGS];
#endif
/*
** Save the callee-save registers; we're going to start using them
** as global registers variables now, which will clobber them,
** and we need to preserve them, because they're callee-save,
** and our caller may need them.
*/
save_regs_to_mem(c_regs);
MR_trace_end();
(*MR_library_finalizer)();
MR_trace_final();
if (MR_profiling) MR_prof_finish();
terminate_engine();
/*
** Restore the callee-save registers before returning,
** since they may be used by the C code that called us.
*/
restore_regs_from_mem(c_regs);
return mercury_exit_status;
}
/*---------------------------------------------------------------------------*/
void mercury_sys_init_wrapper(void); /* suppress gcc warning */
void mercury_sys_init_wrapper(void) {
interpreter_module();
}