Files
mercury/runtime/mercury_wrapper.c
Mark Brown 07cad7c169 If calling from the internal debugger, use readline input for the
Estimated hours taken: 6

If calling from the internal debugger, use readline input for the
interactive term browser and interactive queries.

browser/browse.m:
	Change some if-then-elses to switches, which will help
	catch errors if a new functor is added to the debugger type.

browser/parse.m:
browser/util.m:
	Return a string from util__trace_getline/4 rather than a
	list of chars, which saves converting from a string to a list
	of chars and then back again.

browser/util.m:
	Add a version of util__trace_getline that also takes I/O
	stream arguments.  Pass these arguments to MR_trace_getline.

browser/declarative_oracle.m:
	Call util__trace_getline/4 to do input via readline (if
	available).  Improve error handling.

browser/interactive_query.m:
	Call util__trace_getline to get user input, instead of
	standard library predicates.

runtime/mercury_init.h:
runtime/mercury_wrapper.c:
runtime/mercury_wrapper.h:
trace/mercury_trace_internal.c:
trace/mercury_trace_internal.h:
	Add two I/O stream arguments to MR_trace_getline.
1999-05-30 03:55:13 +00:00

1117 lines
26 KiB
C

/*
INIT mercury_sys_init_wrapper
ENDINIT
*/
/*
** Copyright (C) 1994-1999 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 <string.h>
#include "mercury_getopt.h"
#include "mercury_timing.h"
#include "mercury_init.h"
#include "mercury_dummy.h"
#include "mercury_stack_layout.h"
#include "mercury_trace_base.h"
#include "mercury_memory.h" /* for MR_copy_string() */
/* 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) */
#ifdef MR_DEBUG_AGC
size_t heap_size = 128;
#else
size_t heap_size = 4096;
#endif
size_t detstack_size = 4096;
size_t nondstack_size = 128;
size_t solutions_heap_size = 1024;
size_t global_heap_size = 1024;
size_t trail_size = 128;
size_t debug_heap_size = 4096;
size_t generatorstack_size = 32;
size_t cutstack_size = 32;
/* size of the redzones at the end of data areas, in kilobytes */
/* (but we later multiply by 1024 to convert to bytes) */
#ifdef NATIVE_GC
size_t heap_zone_size = 96;
#else
size_t heap_zone_size = 16;
#endif
size_t detstack_zone_size = 16;
size_t nondstack_zone_size = 16;
size_t solutions_heap_zone_size = 16;
size_t global_heap_zone_size = 16;
size_t trail_zone_size = 16;
size_t debug_heap_zone_size = 16;
size_t generatorstack_zone_size = 16;
size_t cutstack_zone_size = 16;
/* primary cache size to optimize for, in bytes */
size_t pcache_size = 8192;
/* file names for mdb's debugger I/O streams */
const char *MR_mdb_in_filename = NULL;
const char *MR_mdb_out_filename = NULL;
const char *MR_mdb_err_filename = NULL;
/* other options */
bool check_space = FALSE;
static bool benchmark_all_solns = FALSE;
static bool use_own_timer = FALSE;
static int repeats = 1;
unsigned MR_num_threads = 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 -> trace -> browser -> 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);
char * (*MR_address_of_trace_getline)(const char *, FILE *, FILE *);
#ifdef MR_USE_EXTERNAL_DEBUGGER
void (*MR_address_of_trace_init_external)(void);
void (*MR_address_of_trace_final_external)(void);
#endif
#ifdef MR_USE_DECLARATIVE_DEBUGGER
void (*MR_address_of_edt_root_node)(Word, Word *);
#endif
#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) */
void (*MR_io_stderr_stream)(Word *);
void (*MR_io_stdout_stream)(Word *);
void (*MR_io_stdin_stream)(Word *);
void (*MR_io_print_to_cur_stream)(Word, Word);
void (*MR_io_print_to_stream)(Word, Word, Word);
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 *);
/*
** This variable has been replaced by MR_io_print_to_*_stream,
** but the installed mkinit executable may still generate references to it.
** We must therefore keep it until all obsolete mkinit executables have
** been retired.
*/
Code *MR_library_trace_browser;
Code *(*MR_trace_func_ptr)(const MR_Stack_Layout_Label *, MR_Trace_Port,
Unsigned, Unsigned, const char *, int);
void (*MR_register_module_layout)(const MR_Module_Layout *);
#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(&MR_runqueue_head);
/* 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 */
#ifndef MR_THREAD_SAFE
init_thread(MR_use_now);
#else
{
int i;
init_thread_stuff();
init_thread(MR_use_now);
MR_exit_now = FALSE;
for (i = 1 ; i < MR_num_threads ; i++)
create_thread(NULL);
}
#endif
/* 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)();
save_context(&(MR_ENGINE(context)));
/*
** 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(MR_isspace(*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' && !MR_isspace(*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(MR_isspace(*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' && !MR_isspace(*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) {
const char *cmd;
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 "mercury_runtime") at the start of the
** options before passing them to make_argv() and then
** to getopt().
*/
cmd = "mercury_runtime ";
dummy_command_line = make_many(char,
strlen(options) + strlen(cmd) + 1);
strcpy(dummy_command_line, cmd);
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);
}
}
enum MR_long_option {
MR_HEAP_SIZE,
MR_DETSTACK_SIZE,
MR_NONDETSTACK_SIZE,
MR_TRAIL_SIZE,
MR_HEAP_REDZONE_SIZE,
MR_DETSTACK_REDZONE_SIZE,
MR_NONDETSTACK_REDZONE_SIZE,
MR_TRAIL_REDZONE_SIZE,
MR_MDB_TTY,
MR_MDB_IN,
MR_MDB_OUT,
MR_MDB_ERR
};
struct MR_option MR_long_opts[] = {
{ "heap-size", 1, 0, MR_HEAP_SIZE },
{ "detstack-size", 1, 0, MR_DETSTACK_SIZE },
{ "nondetstack-size", 1, 0, MR_NONDETSTACK_SIZE },
{ "trail-size", 1, 0, MR_TRAIL_SIZE },
{ "heap-redzone-size", 1, 0, MR_HEAP_REDZONE_SIZE },
{ "detstack-redzone-size", 1, 0, MR_DETSTACK_REDZONE_SIZE },
{ "nondetstack-redzone-size", 1, 0, MR_NONDETSTACK_REDZONE_SIZE },
{ "trail-redzone-size", 1, 0, MR_TRAIL_REDZONE_SIZE },
{ "mdb-tty", 1, 0, MR_MDB_TTY },
{ "mdb-in", 1, 0, MR_MDB_IN },
{ "mdb-out", 1, 0, MR_MDB_OUT },
{ "mdb-err", 1, 0, MR_MDB_ERR }
};
static void
process_options(int argc, char **argv)
{
unsigned long size;
int c;
int long_index;
while ((c = MR_getopt_long(argc, argv, "acC:d:e:D:i:m:o:P:pr:tT:x",
MR_long_opts, &long_index)) != EOF)
{
switch (c)
{
case MR_HEAP_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
heap_size = size;
break;
case MR_DETSTACK_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
detstack_size = size;
break;
case MR_NONDETSTACK_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
nondstack_size = size;
break;
case MR_TRAIL_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
trail_size = size;
break;
case MR_HEAP_REDZONE_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
heap_zone_size = size;
break;
case MR_DETSTACK_REDZONE_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
detstack_zone_size = size;
break;
case MR_NONDETSTACK_REDZONE_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
nondstack_zone_size = size;
break;
case MR_TRAIL_REDZONE_SIZE:
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
trail_zone_size = size;
break;
case 'i':
case MR_MDB_IN:
MR_mdb_in_filename = MR_copy_string(MR_optarg);
break;
case 'o':
case MR_MDB_OUT:
MR_mdb_out_filename = MR_copy_string(MR_optarg);
break;
case 'e':
case MR_MDB_ERR:
MR_mdb_err_filename = MR_copy_string(MR_optarg);
break;
case 'm':
case MR_MDB_TTY:
MR_mdb_in_filename = MR_copy_string(MR_optarg);
MR_mdb_out_filename = MR_copy_string(MR_optarg);
MR_mdb_err_filename = MR_copy_string(MR_optarg);
break;
case 'a':
benchmark_all_solns = TRUE;
break;
case 'c':
check_space = TRUE;
break;
case 'C':
if (sscanf(MR_optarg, "%lu", &size) != 1)
usage();
pcache_size = size * 1024;
break;
case 'd':
if (streq(MR_optarg, "b"))
MR_nondstackdebug = TRUE;
else if (streq(MR_optarg, "c"))
MR_calldebug = TRUE;
else if (streq(MR_optarg, "d"))
MR_detaildebug = TRUE;
else if (streq(MR_optarg, "g"))
MR_gotodebug = TRUE;
else if (streq(MR_optarg, "G"))
#ifdef CONSERVATIVE_GC
GC_quiet = FALSE;
#else
; /* ignore inapplicable option */
#endif
else if (streq(MR_optarg, "s"))
MR_detstackdebug = TRUE;
else if (streq(MR_optarg, "h"))
MR_heapdebug = TRUE;
else if (streq(MR_optarg, "f"))
MR_finaldebug = TRUE;
else if (streq(MR_optarg, "p"))
MR_progdebug = TRUE;
else if (streq(MR_optarg, "m"))
MR_memdebug = TRUE;
else if (streq(MR_optarg, "r"))
MR_sregdebug = TRUE;
else if (streq(MR_optarg, "t"))
MR_tracedebug = TRUE;
else if (streq(MR_optarg, "S"))
MR_tablestackdebug = TRUE;
else if (streq(MR_optarg, "T"))
MR_tabledebug = TRUE;
else if (streq(MR_optarg, "a")) {
MR_calldebug = TRUE;
MR_nondstackdebug = TRUE;
MR_detstackdebug = TRUE;
MR_heapdebug = TRUE;
MR_gotodebug = TRUE;
MR_sregdebug = TRUE;
MR_finaldebug = TRUE;
MR_tracedebug = TRUE;
#ifdef CONSERVATIVE_GC
GC_quiet = FALSE;
#endif
}
else
usage();
use_own_timer = FALSE;
break;
case 'D':
MR_trace_enabled = TRUE;
if (streq(MR_optarg, "i"))
MR_trace_handler = MR_TRACE_INTERNAL;
#ifdef MR_USE_EXTERNAL_DEBUGGER
else if (streq(MR_optarg, "e"))
MR_trace_handler = MR_TRACE_EXTERNAL;
#endif
else
usage();
break;
case 'p':
MR_profiling = FALSE;
break;
case 'P':
#ifdef MR_THREAD_SAFE
if (sscanf(MR_optarg, "%u", &MR_num_threads) != 1)
usage();
if (MR_num_threads < 1)
usage();
#endif
break;
case 'r':
if (sscanf(MR_optarg, "%d", &repeats) != 1)
usage();
break;
case 't':
use_own_timer = TRUE;
MR_calldebug = FALSE;
MR_nondstackdebug = FALSE;
MR_detstackdebug = FALSE;
MR_heapdebug = FALSE;
MR_gotodebug = FALSE;
MR_sregdebug = FALSE;
MR_finaldebug = FALSE;
break;
case 'T':
if (streq(MR_optarg, "r")) {
MR_time_profile_method = MR_profile_real_time;
} else if (streq(MR_optarg, "v")) {
MR_time_profile_method = MR_profile_user_time;
} else if (streq(MR_optarg, "p")) {
MR_time_profile_method =
MR_profile_user_plus_system_time;
} else {
usage();
}
break;
case 'x':
#ifdef CONSERVATIVE_GC
GC_dont_gc = TRUE;
#endif
break;
default:
usage();
} /* end switch */
} /* end while */
if (MR_optind != argc) {
printf("The MERCURY_OPTIONS environment variable contains "
"the word `%s'\n"
"which is not an option. Please refer to the "
"Environment Variables section\n"
"of the Mercury user's guide for details.\n",
argv[MR_optind]);
fflush(stdout);
exit(1);
}
} /* end process_options() */
static void
usage(void)
{
printf("The MERCURY_OPTIONS environment variable "
"contains an invalid option.\n"
"Please refer to the Environment Variables section of "
"the Mercury\nuser's guide for details.\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
MR_ENGINE(heap_zone)->max = MR_ENGINE(heap_zone)->min;
#endif
MR_CONTEXT(detstack_zone)->max = MR_CONTEXT(detstack_zone)->min;
MR_CONTEXT(nondetstack_zone)->max = MR_CONTEXT(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 (MR_detaildebug) {
debugregs("after final call");
}
#ifdef MR_LOWLEVEL_DEBUG
if (MR_memdebug) {
printf("\n");
#ifndef CONSERVATIVE_GC
printf("max heap used: %6ld words\n",
(long) (MR_ENGINE(heap_zone)->max
- MR_ENGINE(heap_zone)->min));
#endif
printf("max detstack used: %6ld words\n",
(long)(MR_CONTEXT(detstack_zone)->max
- MR_CONTEXT(detstack_zone)->min));
printf("max nondstack used: %6ld words\n",
(long) (MR_CONTEXT(nondetstack_zone)->max
- MR_CONTEXT(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("MR_succip");
break;
case HP_RN:
printf("MR_hp");
break;
case SP_RN:
printf("MR_sp");
break;
case CF_RN:
printf("MR_curfr");
break;
case MF_RN:
printf("MR_maxfr");
break;
case MR_TRAIL_PTR_RN:
printf("MR_trail_ptr");
break;
case MR_TICKET_COUNTER_RN:
printf("MR_ticket_counter");
break;
case MR_SOL_HP_RN:
printf("MR_sol_hp");
break;
case MR_MIN_HP_REC:
printf("MR_min_hp_rec");
break;
case MR_MIN_SOL_HP_REC:
printf("MR_min_sol_hp_rec");
break;
case MR_GLOBAL_HP_RN:
printf("MR_global_hp");
break;
case MR_GEN_STACK_RN:
printf("MR_gen_stack");
break;
case MR_GEN_NEXT_RN:
printf("MR_gen_next");
break;
case MR_CUT_STACK_RN:
printf("MR_cut_stack");
break;
case MR_CUT_NEXT_RN:
printf("MR_cut_next");
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);
BEGIN_MODULE(interpreter_module)
init_entry_ai(do_interpreter);
init_label_ai(global_success);
init_label_ai(global_fail);
init_label_ai(all_done);
BEGIN_CODE
Define_entry(do_interpreter);
MR_incr_sp(3);
MR_stackvar(1) = (Word) MR_hp;
MR_stackvar(2) = (Word) MR_succip;
MR_stackvar(3) = (Word) MR_maxfr;
MR_mkframe("interpreter", 1, LABEL(global_fail));
MR_nondet_stack_trace_bottom = MR_maxfr;
MR_stack_trace_bottom = LABEL(global_success);
if (program_entry_point == NULL) {
fatal_error("no program entry point supplied");
}
#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 (MR_finaldebug) {
save_transient_registers();
printregs("global succeeded");
if (MR_detaildebug)
dumpnondstack();
}
#endif
if (benchmark_all_solns)
redo();
else
GOTO_LABEL(all_done);
Define_label(global_fail);
#ifdef MR_LOWLEVEL_DEBUG
if (MR_finaldebug) {
save_transient_registers();
printregs("global failed");
if (MR_detaildebug)
dumpnondstack();
}
#endif
Define_label(all_done);
#ifdef PROFILE_TIME
if (MR_profiling) MR_prof_turn_off_time_profiling();
#endif
MR_hp = (Word *) MR_stackvar(1);
MR_succip = (Code *) MR_stackvar(2);
MR_maxfr = (Word *) MR_stackvar(3);
MR_decr_sp(3);
#ifdef MR_LOWLEVEL_DEBUG
if (MR_finaldebug && MR_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();
#ifdef MR_THREAD_SAFE
MR_exit_now = TRUE;
pthread_cond_broadcast(MR_runqueue_cond);
#endif
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();
}