mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-20 03:43:51 +00:00
Estimated hours taken: 6
A general cleanup of the code in the runtime directory, aimed at
formulating a more coherent header file inclusion policy. This change
fixes a couple of bugs that prevented the runtime from compiling in
certain configurations (e.g. on muse) due to missing #includes, and
also fixes a few minor unrelated things such as the use of `size_t'
instead of `unsigned'.
Our header file inclusion policy is that every header file should
#include any other header files needed by the declarations or by the
macros it defines. Cyclic interface dependencies, where two header
files each #include the other, must be avoided (by splitting up header
files into smaller indepdent units, if necessary).
At some stage in the future we should rename all the header files to
`mercury_*.h', to avoid any possible name clashes with system or user
header files.
runtime/Mmake:
Add a new target `check_headers' to check that each
header file is syntactically valid in isolation.
runtime/imp.h:
runtime/mercury_float.h:
runtime/mercury_string.h:
runtime/mercury_types.h:
runtime/calls.h:
Move the code in "imp.h" into new header files.
"imp.h" now contains nothing but #includes.
runtime/conf.h.in:
runtime/*.h:
runtime/{label,prof,prof_mem}.c:
runtime/*.mod:
Update the #includes to reflect the new header file structure.
Add some missing header guards. Add some comments.
Put the correct years in most of the copyright notices.
runtime/heap.h:
Fix a bug: add #include "context.h", needed for
min_heap_reclamation_point.
runtime/context.h:
Fix a bug: add #include "memory.h", needed for MemoryZone.
Move the general description comment to the top of the file.
Fix the indentation of some comments. Add a couple of new comments.
runtime/context.mod:
Delete a couple of unnecessary declarations.
runtime/wrapper.mod:
Change the type used for memory sizes from `unsigned' to `size_t'.
Change the `-p' (primary cache size) option so that it is always
a size in kilobytes, rather than being schitzophrenic about
whether it is bytes or kilobytes.
runtime/regorder_base.h:
Removed, since it not used (and constitutes a
double-maintenance problem).
814 lines
18 KiB
Modula-2
814 lines
18 KiB
Modula-2
/*
|
|
** Copyright (C) 1995 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: wrapper.mod
|
|
** main authors: zs, fjh
|
|
**
|
|
** This file contains the startup code for the Mercury runtime.
|
|
** It defines mercury_runtime_main(), which is invoked from
|
|
** mercury_main() in the C file generated by util/mkinit.c.
|
|
** The code for mercury_runtime_main() initializes various things,
|
|
** processes options (which are specified via an environment variable)
|
|
** and then invokes start_mercury_engine() to start execution.
|
|
*/
|
|
|
|
#include "imp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#include "timing.h"
|
|
#include "getopt.h"
|
|
#include "init.h"
|
|
#include "dummy.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 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;
|
|
|
|
/* 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 */
|
|
|
|
int r1val = -1;
|
|
int r2val = -1;
|
|
int r3val = -1;
|
|
|
|
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;
|
|
|
|
const char * progname;
|
|
int mercury_argc; /* not counting progname */
|
|
char ** mercury_argv;
|
|
int mercury_exit_status = 0;
|
|
|
|
/*
|
|
** Constraint solver trail.
|
|
**
|
|
** XXX this should not be here; it should be in engine.mod
|
|
** or constraints.c or somewhere like that.
|
|
*/
|
|
#ifdef CONSTRAINTS
|
|
int *mercury_solver_sp;
|
|
int *mercury_solver_sp_old;
|
|
size_t solver_ticket_stack_size = SOLVER_STACK_SIZE;
|
|
#endif
|
|
|
|
/*
|
|
** The Mercury runtime calls io:run/0 in the Mercury library, and the Mercury
|
|
** library calls main/2 in the user's program. The Mercury runtime also calls
|
|
** init_gc() and init_modules() which are in the automatically generated
|
|
** C init file, and mercury_init_io(), which is in the Mercury library.
|
|
**
|
|
** But to enable Quickstart of shared libraries on Irix 5,
|
|
** 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 *library_entry_point; /* normally io:run/0 (mercury__io__run_0_0) */
|
|
Code *program_entry_point; /* normally main/2 (mercury__main_2_0) */
|
|
|
|
|
|
#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 run_code(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);
|
|
|
|
int mercury_runtime_main(int argc, char **argv)
|
|
{
|
|
#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);
|
|
|
|
#ifndef SPEED
|
|
/*
|
|
** 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;
|
|
|
|
/* double-check that the garbage collector knows about
|
|
global variables in shared libraries */
|
|
GC_is_visible(fake_reg);
|
|
|
|
/* call the init_gc() function defined in <foo>_init.c - */
|
|
/* this is to work around a Solaris 2.X (X <= 4) linker bug */
|
|
(*address_of_init_gc)();
|
|
|
|
/* 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 vars */
|
|
process_args(argc, argv);
|
|
process_environment_options();
|
|
|
|
#if (defined(USE_GCC_NONLOCAL_GOTOS) && !defined(USE_ASM_LABELS)) || \
|
|
defined(PROFILE_CALLS) || defined(PROFILE_TIME)
|
|
do_init_modules();
|
|
#endif
|
|
|
|
(*address_of_mercury_init_io)();
|
|
|
|
#ifdef CONSTRAINTS
|
|
perform_solver_initialisations();
|
|
/* convert the stack size to bytes from kb */
|
|
solver_ticket_stack_size *= 1024;
|
|
mercury_solver_sp =checked_malloc(solver_ticket_stack_size*sizeof(int));
|
|
mercury_solver_sp_old = mercury_solver_sp;
|
|
#endif
|
|
|
|
/* execute the selected entry point */
|
|
init_engine();
|
|
run_code();
|
|
|
|
/*
|
|
** 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 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++;
|
|
}
|
|
|
|
/*
|
|
** 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';
|
|
}
|
|
|
|
*args_ptr = args;
|
|
*argv_ptr = argv;
|
|
*argc_ptr = argc;
|
|
}
|
|
|
|
|
|
/**
|
|
** 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, "acd:hLlp:r:s:tw:xz:1:2:3:")) != EOF)
|
|
{
|
|
switch (c)
|
|
{
|
|
|
|
case 'a': benchmark_all_solns = TRUE;
|
|
break;
|
|
|
|
case 'c': check_space = TRUE;
|
|
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 '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':
|
|
if (sscanf(optarg, "%lu", &size) != 1)
|
|
usage();
|
|
|
|
pcache_size = size * 1024;
|
|
|
|
break;
|
|
|
|
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 CONSTRAINTS
|
|
else if (optarg[0] == 's')
|
|
solver_ticket_stack_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 '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);
|
|
}
|
|
|
|
library_entry_point = which_label->e_addr;
|
|
|
|
break;
|
|
}
|
|
case 'm': {
|
|
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 = 1;
|
|
#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;
|
|
else
|
|
usage();
|
|
|
|
break;
|
|
|
|
case '1': if (sscanf(optarg, "%d", &r1val) != 1)
|
|
usage();
|
|
|
|
break;
|
|
|
|
case '2': if (sscanf(optarg, "%d", &r2val) != 1)
|
|
usage();
|
|
|
|
break;
|
|
|
|
case '3': if (sscanf(optarg, "%d", &r3val) != 1)
|
|
usage();
|
|
|
|
break;
|
|
|
|
default: usage();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usage(void)
|
|
{
|
|
printf("Mercury runtime usage:\n"
|
|
"MERCURY_OPTIONS=\"[-hclt] [-d[abcdghs]] [-[sz][hdn]#]\n"
|
|
" [-p#] [-r#] [-1#] [-2#] [-3#] [-w name] [-m name]\"\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\tuse own timer\n"
|
|
"-x \t\tdisable garbage collection\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"
|
|
"-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 CONSTRAINTS
|
|
"-ss<n> \t\tallocate n kb for the solver ticket stack\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"
|
|
"-p<n> \t\tprimary cache size in kbytes\n"
|
|
"-r<n> \t\trepeat n times\n"
|
|
"-m<name> \tcall I/O predicate with given name (default: main/2)\n"
|
|
"-w<name> \tcall predicate with given name\n"
|
|
"-1<x> \t\tinitialize register r1 with value x\n"
|
|
"-2<x> \t\tinitialize register r2 with value x\n"
|
|
"-3<x> \t\tinitialize register r3 with value x\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
|
|
void run_code(void)
|
|
{
|
|
static int repcounter;
|
|
|
|
#if !defined(SPEED) && 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
|
|
*/
|
|
|
|
unsigned char safety_buffer[SAFETY_BUFFER_SIZE];
|
|
|
|
global_pointer_2 = safety_buffer; /* defeat optimization */
|
|
memset(safety_buffer, MAGIC_MARKER_2, SAFETY_BUFFER_SIZE);
|
|
#endif
|
|
|
|
#ifndef SPEED
|
|
#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 = get_run_time();
|
|
time_at_last_stat = time_at_start;
|
|
|
|
for (repcounter = 0; repcounter < repeats; repcounter++)
|
|
{
|
|
debugmsg0("About to call engine\n");
|
|
start_mercury_engine(ENTRY(do_interpreter));
|
|
debugmsg0("Returning from start_mercury_engine\n");
|
|
}
|
|
|
|
if (use_own_timer)
|
|
time_at_finish = get_run_time();
|
|
|
|
#if defined(USE_GCC_NONLOCAL_GOTOS) && !defined(SPEED)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SAFETY_BUFFER_SIZE; i++)
|
|
assert(safety_buffer[i] == MAGIC_MARKER_2);
|
|
}
|
|
#endif
|
|
|
|
if (detaildebug) {
|
|
debugregs("after final call");
|
|
}
|
|
|
|
#ifndef SPEED
|
|
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);
|
|
}
|
|
|
|
#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;
|
|
default:
|
|
printf("UNKNOWN%d", i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("\t%lu\n", num_uses[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
BEGIN_MODULE(interpreter_module)
|
|
|
|
BEGIN_CODE
|
|
|
|
do_interpreter:
|
|
push(hp);
|
|
push(succip);
|
|
push(maxfr);
|
|
mkframe("interpreter", 1, LABEL(global_fail));
|
|
|
|
if (library_entry_point == NULL) {
|
|
fatal_error("no library entry point supplied");
|
|
}
|
|
noprof_call(library_entry_point, LABEL(global_success));
|
|
|
|
global_success:
|
|
#ifndef SPEED
|
|
if (finaldebug) {
|
|
save_transient_registers();
|
|
printregs("global succeeded");
|
|
if (detaildebug)
|
|
dumpnondstack();
|
|
}
|
|
#endif
|
|
|
|
if (benchmark_all_solns)
|
|
redo();
|
|
else
|
|
GOTO_LABEL(all_done);
|
|
|
|
global_fail:
|
|
#ifndef SPEED
|
|
if (finaldebug) {
|
|
save_transient_registers();
|
|
printregs("global failed");
|
|
|
|
if (detaildebug)
|
|
dumpnondstack();
|
|
}
|
|
#endif
|
|
|
|
all_done:
|
|
maxfr = (Word *) pop();
|
|
succip = (Code *) pop();
|
|
hp = (Word *) pop();
|
|
|
|
#ifndef SPEED
|
|
if (finaldebug && detaildebug) {
|
|
save_transient_registers();
|
|
printregs("after popping...");
|
|
}
|
|
#endif
|
|
proceed();
|
|
#ifndef USE_GCC_NONLOCAL_GOTOS
|
|
return 0;
|
|
#endif
|
|
|
|
END_MODULE
|