mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
3083 lines
100 KiB
C
3083 lines
100 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 1994-2011 The University of Melbourne.
|
|
// Copyright (C) 2014-2016, 2018, 2020-2026 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// 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
|
|
// MR_call_engine(MR_do_interpreter), which invokes main/2.
|
|
//
|
|
// It also defines mercury_runtime_terminate(), which performs
|
|
// various cleanups that are needed to terminate cleanly.
|
|
|
|
/*
|
|
INIT mercury_sys_init_wrapper
|
|
ENDINIT
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
#include "mercury_deep_profiling.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifdef MR_HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef MR_MSVC_STRUCTURED_EXCEPTIONS
|
|
#include <excpt.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_FENV_H
|
|
#include <fenv.h>
|
|
#endif
|
|
|
|
#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_deep_profiling.h"
|
|
#include "mercury_memory.h" // for MR_copy_string()
|
|
#include "mercury_memory_handlers.h" // for MR_default_handler
|
|
#include "mercury_runtime_util.h" // for MR_get_program_basename()
|
|
#include "mercury_thread.h" // for MR_debug_threads
|
|
#include "mercury_threadscope.h"
|
|
|
|
// global variables concerned with testing (i.e. not with the engine)
|
|
|
|
// command-line options
|
|
|
|
// Sizes of data areas (including redzones), in kilobytes
|
|
// (but we later multiply by 1024 to convert to bytes, then make sure they are
|
|
// at least as big as the primary cache size then round up to the page size).
|
|
//
|
|
// XXX We should associate a *single* unit with each of these sizes, and
|
|
// use that unit *consistently*; anything else is just asking for trouble.
|
|
// To reduce the chances of trouble further, include the name of the unit
|
|
// in the name of the variable.
|
|
//
|
|
// Note that it is OK to allocate a large heap, since we will only touch
|
|
// the part of it that we use; we are really only allocating address space,
|
|
// not physical memory. But the other areas should be kept small, at least
|
|
// in the case when conservative GC is enabled, since the conservative GC
|
|
// will scan them.
|
|
//
|
|
// Note that for the accurate collector, the total heap size that we use
|
|
// will be twice the heap size specified here, since it is a two-space
|
|
// collector.
|
|
//
|
|
// Changes to MR_heap_size, MR_detstack_size or MR_nondetstack_size should be
|
|
// reflected in the user guide. Changes to MR_heap_size may also require
|
|
// changing MR_heap_zone_size and/or the MR_heap_margin_size, which are
|
|
// defined below.
|
|
|
|
#ifdef MR_DEBUG_AGC_SMALL_HEAP
|
|
size_t MR_heap_size = 13 * sizeof(MR_Word);
|
|
#else
|
|
size_t MR_heap_size = 8192 * sizeof(MR_Word);
|
|
#endif
|
|
#ifdef MR_STACK_SEGMENTS
|
|
size_t MR_detstack_size = 64 * sizeof(MR_Word);
|
|
size_t MR_nondetstack_size = 16 * sizeof(MR_Word);
|
|
#else
|
|
// If you change these, you should also change the description of the
|
|
// default stack sizes in doc/user_guide.texi.
|
|
size_t MR_detstack_size = 4096 * sizeof(MR_Word);
|
|
size_t MR_nondetstack_size = 64 * sizeof(MR_Word);
|
|
size_t MR_small_detstack_size = 512 * sizeof(MR_Word);
|
|
size_t MR_small_nondetstack_size = 8 * sizeof(MR_Word);
|
|
#endif
|
|
size_t MR_solutions_heap_size = 256 * sizeof(MR_Word);
|
|
size_t MR_global_heap_size = 256 * sizeof(MR_Word);
|
|
size_t MR_trail_size = 32 * sizeof(MR_Word);
|
|
size_t MR_debug_heap_size = 1024 * sizeof(MR_Word);
|
|
size_t MR_genstack_size = 8 * sizeof(MR_Word);
|
|
size_t MR_cutstack_size = 8 * sizeof(MR_Word);
|
|
size_t MR_pnegstack_size = 8 * sizeof(MR_Word);
|
|
size_t MR_gen_detstack_size = 16 * sizeof(MR_Word);
|
|
size_t MR_gen_nondetstack_size = 16 * sizeof(MR_Word);
|
|
|
|
// size of the redzones at the end of data areas, in kilobytes
|
|
//
|
|
// For accurate GC, although we start out with a big heap (32 Mb on 32-bit
|
|
// architectures -- see above), we don't want to touch all of it unless we
|
|
// really need to. So with accurate GC in LLDS grades, we start out with
|
|
// a 28 Mb redzone, leaving an active heap size of 4Mb. The collector
|
|
// should (XXX it currently doesn't) resize this redzone automatically at
|
|
// the end of each collection.
|
|
//
|
|
// For MLDS grades, we don't use redzones to schedule GC;
|
|
// instead GCs are scheduled, based on MR_heap_margin_size (see below),
|
|
// by explicit calls to MR_GC_check()
|
|
|
|
#if defined(MR_NATIVE_GC) && !defined(MR_HIGHLEVEL_CODE)
|
|
#ifdef MR_DEBUG_AGC_SMALL_HEAP
|
|
size_t MR_heap_zone_size = 8 * sizeof(MR_Word);
|
|
#else
|
|
size_t MR_heap_zone_size = (4 + 7 * 1024) * sizeof(MR_Word);
|
|
#endif
|
|
#else
|
|
size_t MR_heap_zone_size = 4 * sizeof(MR_Word);
|
|
#endif
|
|
#ifdef MR_STACK_SEGMENTS
|
|
// We don't use redzones with stack segments.
|
|
|
|
size_t MR_detstack_zone_size = 0;
|
|
size_t MR_nondetstack_zone_size = 0;
|
|
#else
|
|
size_t MR_detstack_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_nondetstack_zone_size = 4 * sizeof(MR_Word);
|
|
#endif
|
|
size_t MR_solutions_heap_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_global_heap_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_trail_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_debug_heap_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_genstack_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_cutstack_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_pnegstack_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_gen_detstack_zone_size = 4 * sizeof(MR_Word);
|
|
size_t MR_gen_nondetstack_zone_size = 4 * sizeof(MR_Word);
|
|
|
|
double MR_heap_expansion_factor = 2.0;
|
|
|
|
// MR_heap_margin_size is used for accurate GC with the MLDS->C back-end.
|
|
// It is used to decide when to actually do a garbage collection.
|
|
// At each call to MR_GC_check(), which is normally done before
|
|
// each allocation, we check whether there is less than this
|
|
// amount of heap space still available, and if not, we call
|
|
// MR_garbage_collect().
|
|
//
|
|
// XXX Actually, this variable is only used to set the initial value
|
|
// of heap_zone->gc_threshold.
|
|
// The collector recomputes heap_zone->gc_threshold automatically at
|
|
// the end of each collection.
|
|
//
|
|
// Like the sizes above, it is measured in kilobytes
|
|
// (but we later multiply by 1024 to convert to bytes).
|
|
|
|
#ifdef MR_DEBUG_AGC_SMALL_HEAP
|
|
size_t MR_heap_margin_size = 4 * sizeof(MR_Word);
|
|
#else
|
|
size_t MR_heap_margin_size = 7 * 1024 * sizeof(MR_Word);
|
|
#endif
|
|
|
|
// When we use stack segments, we reserve the last MR_stack_margin_size_words
|
|
// words of each stack segment for leaf procedures. This way, leaf procedures
|
|
// that do not need this much stack space can allocate their stack space
|
|
// *without* incurring the cost of a test.
|
|
//
|
|
// MR_stack_margin_size is never consulted directly; instead, its value is used
|
|
// to set the MR_zone_extend_threshold field in a stack's memory zone.
|
|
//
|
|
// The value of MR_stack_margin_size_words should always match the value of
|
|
// max_leaf_stack_frame_size in compiler/llds_out_instr.m.
|
|
|
|
size_t MR_stack_margin_size_words = 32;
|
|
|
|
// Primary cache size to optimize for, in bytes.
|
|
size_t MR_pcache_size = 8192;
|
|
|
|
// Limits on the number of contexts we can create for parallel execution.
|
|
// These allow 64MB of det stacks regardless of which grade is being used.
|
|
// Where sizeof(MR_Word) 8 and the detstack is 64 and 4098 Kwords big
|
|
// for stseg and non stseg grades.
|
|
|
|
#ifdef MR_STACK_SEGMENTS
|
|
MR_Unsigned MR_max_contexts_per_thread = 128;
|
|
#else
|
|
MR_Unsigned MR_max_contexts_per_thread = 2;
|
|
#endif
|
|
MR_Unsigned MR_max_outstanding_contexts;
|
|
|
|
// The number of contexts per loop control per thread.
|
|
|
|
MR_Unsigned MR_num_contexts_per_loop_control_per_thread = 4;
|
|
MR_Unsigned MR_num_contexts_per_loop_control;
|
|
|
|
// 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;
|
|
MR_bool MR_mdb_in_window = MR_FALSE;
|
|
|
|
MR_bool MR_mdb_decl_print_progress = MR_TRUE;
|
|
MR_bool MR_mdb_benchmark_silent = MR_FALSE;
|
|
|
|
// Use readline() in the debugger even if the input stream is not a tty?
|
|
MR_bool MR_force_readline = MR_FALSE;
|
|
|
|
// Low level debugging options.
|
|
//
|
|
// If MR_watch_addr is not NULL, then the some of the low level debugging
|
|
// messages will print the value it points to.
|
|
//
|
|
// If MR_watch_csd_addr is not NULL, then the some of the low level debugging
|
|
// messages will print the MR_CallSiteDynamic structure it points to. Since
|
|
// this structure is typically in memory that not part of the address space of
|
|
// the program at startup, this printing will be inhibited until
|
|
// MR_watch_csd_started is set to true, which will happen when you call a
|
|
// procedure whose entry label matches the string in MR_watch_csd_start_name.
|
|
//
|
|
// If the low level debugging of calls is enabled, MR_lld_cur_call is the
|
|
// sequence number of the last call executed by the program.
|
|
//
|
|
// Getting low level debugging messages from *every* call, *every* heap
|
|
// allocation etc usually results in an avalanche of data that buries the
|
|
// information you are looking for, and often runs filesystems out of space.
|
|
// Therefore we inhibit these messages unless any one of four conditions
|
|
// apply. We implement this by making MR_lld_print_enabled, which controls
|
|
// the printing of these messages, the logical OR of MR_lld_print_name_enabled,
|
|
// MR_lld_print_csd_enabled, MR_lld_print_region_enabled and
|
|
// MR_lld_debug_enabled, which are flags implementing the four conditions.
|
|
// (We rely on these flags being 0 or 1 (i.e. MR_FALSE or MR_TRUE) so we can
|
|
// implement logical OR as bitwise OR, which is faster.)
|
|
//
|
|
// - The first condition is MR_lld_start_block calls starting with a call to a
|
|
// predicate whose entry label matches MR_lld_start_name.
|
|
// - The second condition is MR_lld_start_block calls starting with a call
|
|
// at which the value of the MR_next_call_site_dynamic global variable
|
|
// matches the value in MR_watch_csd_addr.
|
|
// - The third condition is calls whose sequence number is in a range
|
|
// specified by MR_lld_print_more_min_max, which should point to a string
|
|
// containing a comma-separated list of integer intervals (the last interval
|
|
// may be open ended).
|
|
// - The fourth is calls between debugger commands that enable and disable
|
|
// low level messages.
|
|
//
|
|
// MR_lld_start_until and MR_lld_csd_until give the end call numbers of the
|
|
// blocks printed for the first two conditions. MR_lld_print_{min,max} give the
|
|
// boundaries of the (current or next) block for the third condition.
|
|
|
|
MR_Word *MR_watch_addr = NULL;
|
|
MR_CallSiteDynamic *MR_watch_csd_addr = NULL;
|
|
MR_bool MR_watch_csd_started = MR_FALSE;
|
|
const char *MR_watch_csd_start_name = ""; // Must not be NULL.
|
|
|
|
unsigned long MR_lld_cur_call = 0;
|
|
MR_bool MR_lld_print_enabled = MR_FALSE;
|
|
MR_bool MR_lld_print_name_enabled = MR_FALSE;
|
|
MR_bool MR_lld_print_csd_enabled = MR_FALSE;
|
|
MR_bool MR_lld_print_region_enabled = MR_FALSE;
|
|
MR_bool MR_lld_print_always_enabled = MR_FALSE;
|
|
|
|
const char *MR_lld_start_name = ""; // Must not be NULL.
|
|
unsigned MR_lld_start_block = 100; // By default, print stuff
|
|
// for a block of 100 calls.
|
|
unsigned long MR_lld_start_until = (unsigned long) -1;
|
|
|
|
unsigned long MR_lld_csd_until = (unsigned long) -1;
|
|
|
|
unsigned long MR_lld_print_min = (unsigned long) -1;
|
|
unsigned long MR_lld_print_max = 0;
|
|
char *MR_lld_print_more_min_max = NULL;
|
|
|
|
// other options
|
|
|
|
MR_bool MR_check_space = MR_FALSE;
|
|
|
|
#define MAX_MEM_USAGE_REPORT_ATTEMPTS 100
|
|
|
|
static char *MR_mem_usage_report_prefix = NULL;
|
|
|
|
static int MR_num_output_args = 0;
|
|
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
MR_Unsigned MR_num_ws_engines = 0;
|
|
MR_Unsigned MR_max_engines = 1024;
|
|
|
|
MR_Unsigned MR_granularity_wsdeque_length_factor = 8;
|
|
MR_Unsigned MR_granularity_wsdeque_length = 0;
|
|
#endif
|
|
|
|
static MR_bool MR_print_table_statistics = MR_FALSE;
|
|
|
|
// Timing.
|
|
int MR_user_time_at_last_stat;
|
|
int MR_user_time_at_start;
|
|
|
|
int MR_real_time_at_last_stat;
|
|
int MR_real_time_at_start;
|
|
|
|
// Time profiling.
|
|
#if defined(MR_CYGWIN)
|
|
// Other timing methods are not supported on Cygwin.
|
|
enum MR_TimeProfileMethod
|
|
MR_time_profile_method = MR_profile_real_time;
|
|
#else
|
|
enum MR_TimeProfileMethod
|
|
MR_time_profile_method = MR_profile_user_plus_system_time;
|
|
#endif
|
|
|
|
const char *MR_progname;
|
|
MR_bool MR_progname_is_known;
|
|
int mercury_argc; // Not counting progname.
|
|
char **mercury_argv;
|
|
int mercury_exit_status = 0;
|
|
|
|
MR_bool MR_profiling = MR_TRUE;
|
|
MR_bool MR_print_deep_profiling_statistics = MR_FALSE;
|
|
static unsigned MR_deep_prof_random_write = 0;
|
|
static MR_bool MR_deep_profiling_save_results = MR_TRUE;
|
|
static MR_bool MR_complexity_save_results = MR_TRUE;
|
|
|
|
#ifdef MR_TYPE_CTOR_STATS
|
|
|
|
#include "mercury_type_info.h"
|
|
#include "mercury_array_macros.h"
|
|
|
|
typedef struct {
|
|
MR_ConstString type_stat_module;
|
|
MR_ConstString type_stat_name;
|
|
int type_stat_ctor_rep;
|
|
long type_stat_count;
|
|
} MR_TypeNameStat;
|
|
|
|
struct MR_TypeStat_Struct {
|
|
long type_ctor_reps[MR_TYPECTOR_REP_UNKNOWN + 1];
|
|
MR_TypeNameStat *type_ctor_names;
|
|
int type_ctor_name_max;
|
|
int type_ctor_name_next;
|
|
};
|
|
|
|
// We depend on these five structs being initialized to zero.
|
|
// XXX Five?
|
|
MR_TypeStat MR_type_stat_mer_unify;
|
|
MR_TypeStat MR_type_stat_c_unify;
|
|
MR_TypeStat MR_type_stat_mer_compare;
|
|
MR_TypeStat MR_type_stat_c_compare;
|
|
|
|
#endif
|
|
|
|
// 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.
|
|
//
|
|
// In general, to avoid various 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".
|
|
// In the case of the compiler, we insert mdbcomp between browser and library.
|
|
|
|
void (*MR_address_of_mercury_init_io)(void);
|
|
void (*MR_address_of_init_modules)(void);
|
|
void (*MR_address_of_init_modules_type_tables)(void);
|
|
void (*MR_address_of_init_modules_debugger)(void);
|
|
#ifdef MR_RECORD_TERM_SIZES
|
|
void (*MR_address_of_init_modules_complexity)(void);
|
|
#endif
|
|
#ifdef MR_DEEP_PROFILING
|
|
void (*MR_address_of_write_out_proc_statics)(FILE *deep_fp,
|
|
FILE *procrep_fp);
|
|
#endif
|
|
#ifdef MR_THREADSCOPE
|
|
void (*MR_address_of_init_modules_threadscope_string_table)(void);
|
|
#endif
|
|
void (*MR_address_of_init_modules_required)(void);
|
|
void (*MR_address_of_final_modules_required)(void);
|
|
|
|
MR_TypeCtorInfo MR_type_ctor_info_for_univ;
|
|
MR_TypeCtorInfo MR_type_info_for_type_info;
|
|
MR_TypeCtorInfo MR_type_info_for_pseudo_type_info;
|
|
MR_TypeInfo MR_type_info_for_list_of_univ;
|
|
MR_TypeInfo MR_type_info_for_list_of_int;
|
|
MR_TypeInfo MR_type_info_for_list_of_char;
|
|
MR_TypeInfo MR_type_info_for_list_of_string;
|
|
MR_TypeInfo MR_type_info_for_list_of_type_info;
|
|
MR_TypeInfo MR_type_info_for_list_of_pseudo_type_info;
|
|
|
|
char *(*MR_address_of_trace_getline)(const char *, FILE *, FILE *);
|
|
char *(*MR_address_of_trace_get_command)(const char *, FILE *, FILE *);
|
|
const char *(*MR_address_of_trace_browse_all_on_level)(FILE *,
|
|
const MR_LabelLayout *, MR_Word *, MR_Word *, int, MR_bool);
|
|
|
|
#ifdef MR_USE_EXTERNAL_DEBUGGER
|
|
void (*MR_address_of_trace_init_external)(void);
|
|
void (*MR_address_of_trace_final_external)(void);
|
|
#endif
|
|
|
|
#ifdef MR_BOEHM_GC
|
|
static MR_bool MR_gc_disable;
|
|
#endif
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
void (*MR_address_of_init_gc)(void);
|
|
#endif
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
void MR_CALL (*MR_program_entry_point)(void);
|
|
// normally main_2_p_0 (main/2)
|
|
#else
|
|
MR_Code *MR_program_entry_point;
|
|
// normally mercury__main_2_0 (main/2)
|
|
#endif
|
|
|
|
const char *MR_runtime_flags = "";
|
|
|
|
void (*MR_library_initializer)(void);
|
|
// normally ML_std_library_init
|
|
void (*MR_library_finalizer)(void);
|
|
// normally ML_std_library_finalize
|
|
|
|
void (*MR_io_stderr_stream)(MercuryFilePtr *);
|
|
void (*MR_io_stdout_stream)(MercuryFilePtr *);
|
|
void (*MR_io_stdin_stream)(MercuryFilePtr *);
|
|
void (*MR_io_print_to_stream)(MR_Word, MercuryFilePtr, MR_Word);
|
|
|
|
MR_Code *(*MR_exec_trace_func_ptr)(const MR_LabelLayout *);
|
|
|
|
void (*MR_address_of_trace_interrupt_handler)(void);
|
|
|
|
void (*MR_register_module_layout)(const MR_ModuleLayout *);
|
|
|
|
#ifdef MR_RECORD_TERM_SIZES
|
|
MR_ComplexityProc *MR_complexity_procs;
|
|
int MR_num_complexity_procs;
|
|
#endif
|
|
|
|
#ifdef MR_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 MR_process_args(int argc, char **argv);
|
|
static const char *MR_make_argv(const char *, char **, char ***, int *);
|
|
static void MR_process_environment_options(void);
|
|
static void MR_process_options(int argc, char **argv);
|
|
MR_NO_RETURN(static void MR_usage(void));
|
|
static MR_bool MR_matches_exec_name(const char *option);
|
|
|
|
#ifdef MR_TYPE_CTOR_STATS
|
|
static void MR_print_type_ctor_stats(void);
|
|
static void MR_print_one_type_ctor_stat(FILE *fp, const char *op,
|
|
MR_TypeStat *type_stat);
|
|
#endif
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
static void MR_do_interpreter(void);
|
|
#else
|
|
MR_declare_entry(MR_do_interpreter);
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
mercury_runtime_init(int argc, char **argv)
|
|
{
|
|
MR_bool saved_debug_enabled;
|
|
MR_bool saved_trace_count_enabled;
|
|
|
|
#if MR_NUM_REAL_REGS > 0
|
|
MR_Word c_regs[MR_NUM_REAL_REGS];
|
|
#endif
|
|
|
|
// Save the callee-save registers; we are going to start using them
|
|
// as global registers variables now, which will clobber them,
|
|
// and we need to preserve them, because they are callee-save,
|
|
// and our caller may need them ;-)
|
|
|
|
MR_save_regs_to_mem(c_regs);
|
|
|
|
#ifdef __linux__
|
|
// XXX Ensure that we link in atexit().
|
|
// XXX This works around a bug in gcc 2.95.3 (prerelease) and/or
|
|
// libc 2.2.2 on Debian Linux, where we'd get a link error when
|
|
// building libmer_rt.so with --no-undefined, due to a reference
|
|
// to atexit() from crtendS.o, which gets linked last, after any
|
|
// libraries such as `-lc'.
|
|
|
|
MR_global_pointer = (void *) atexit;
|
|
#endif
|
|
|
|
#if defined(MR_DEBUG_THE_RUNTIME) || defined(MR_TABLE_DEBUG)
|
|
if (MR_unbufdebug) {
|
|
// 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
|
|
|
|
#if defined(MR_PROFILE_PARALLEL_EXECUTION_SUPPORT)
|
|
// Setup support for reading the CPU's TSC and detect the clock speed of the
|
|
// processor. This is currently used by profiling of the parallelism
|
|
// runtime and the threadscope support but may be used by other profiling
|
|
// or timing code. On architectures other than i386 and amd64 this is a
|
|
// no-op.
|
|
|
|
MR_do_cpu_feature_detection();
|
|
#endif
|
|
|
|
// This must be done before MR_init_conservative_GC(), to ensure that
|
|
// the GC's signal handler gets installed after our signal handler.
|
|
// This is needed because our signal handler assumes that signals
|
|
// which it can't handle are fatal.
|
|
|
|
MR_setup_signals();
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
MR_init_conservative_GC();
|
|
#endif
|
|
|
|
// Process the command line and the options in the relevant environment
|
|
// variables, and save results in global variables.
|
|
|
|
MR_process_args(argc, argv);
|
|
MR_process_environment_options();
|
|
|
|
#ifdef MR_STACK_FRAME_STATS
|
|
MR_init_stack_frame_stats();
|
|
#endif // MR_STACK_FRAME_STATS
|
|
|
|
// 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_debug_enabled = MR_debug_enabled;
|
|
saved_trace_count_enabled = MR_trace_count_enabled;
|
|
MR_debug_enabled = MR_FALSE;
|
|
MR_trace_count_enabled = MR_FALSE;
|
|
MR_update_trace_func_enabled();
|
|
|
|
#if defined(MR_NEED_INITIALIZATION_AT_START) || defined(MR_MINIMAL_MODEL_DEBUG)
|
|
MR_do_init_modules();
|
|
#endif
|
|
|
|
(*MR_address_of_mercury_init_io)();
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
// MR_init_context_stuff() and MR_init_thread_stuff() must be called prior
|
|
// to MR_init_memory()
|
|
|
|
MR_init_context_stuff();
|
|
MR_init_thread_stuff();
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
MR_max_outstanding_contexts =
|
|
MR_max_contexts_per_thread * MR_num_ws_engines;
|
|
MR_num_contexts_per_loop_control =
|
|
MR_num_contexts_per_loop_control_per_thread * MR_num_ws_engines;
|
|
MR_granularity_wsdeque_length =
|
|
MR_granularity_wsdeque_length_factor * MR_num_ws_engines;
|
|
#endif
|
|
MR_primordial_thread = pthread_self();
|
|
#endif
|
|
|
|
// XXX The condition here used to be
|
|
// #if defined(MR_HIGHLEVEL_CODE) && defined(MR_CONSERVATIVE_GC)
|
|
// and was part of a change by Fergus to remove an unnecessary
|
|
// dependency on the complicated Mercury engine code. Unfortunately
|
|
// this is no longer the case because other such dependencies have
|
|
// since crept in. Using the original condition would cause hlc.par
|
|
// programs to immediately SEGFAULT via reference to an uninitialised
|
|
// Mercury engine.
|
|
|
|
#if 0
|
|
MR_init_memory();
|
|
#ifdef MR_USE_TRAIL
|
|
// initialize the trail
|
|
MR_trail_zone = MR_create_or_reuse_zone("trail",
|
|
MR_trail_size, MR_next_offset(),
|
|
MR_trail_zone_size, MR_default_handler);
|
|
MR_trail_ptr = (MR_TrailEntry *) MR_trail_zone->min;
|
|
MR_ticket_counter = 1;
|
|
MR_ticket_high_water = 1;
|
|
#endif
|
|
#else
|
|
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
#ifdef MR_HAVE_THREAD_PINNING
|
|
MR_pin_primordial_thread();
|
|
#endif
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
// We must setup threadscope before we setup the first engine.
|
|
// Pin the primordial thread, if thread pinning is configured.
|
|
MR_setup_threadscope();
|
|
|
|
// Setup the threadscope string tables before the standard library is
|
|
// initialised or engines are created.
|
|
(*MR_address_of_init_modules_threadscope_string_table)();
|
|
#endif
|
|
#endif
|
|
|
|
// Start up the Mercury engine. We don't yet know how many slots will be
|
|
// needed for thread-local mutable values so allocate the maximum number.
|
|
MR_init_thread_inner(MR_use_now, MR_PRIMORIDAL_ENGINE_TYPE);
|
|
MR_SET_THREAD_LOCAL_MUTABLES(
|
|
MR_create_thread_local_mutables(MR_MAX_THREAD_LOCAL_MUTABLES));
|
|
|
|
// Start up additional work-stealing Mercury engines.
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < MR_num_ws_engines; i++) {
|
|
MR_create_worksteal_thread();
|
|
}
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
// TSC Synchronization is not used, support is commented out.
|
|
// See runtime/mercury_threadscope.h for an explanation.
|
|
for (i = 1; i < MR_num_threads; i++) {
|
|
MR_threadscope_sync_tsc_master();
|
|
}
|
|
#endif
|
|
|
|
while (MR_num_idle_ws_engines < MR_num_ws_engines-1) {
|
|
// busy wait until the worker threads are ready
|
|
MR_ATOMIC_PAUSE;
|
|
}
|
|
}
|
|
|
|
#ifdef MR_HAVE_THREAD_PINNING
|
|
MR_done_thread_pinning();
|
|
#endif
|
|
#endif // ! MR_LL_PARALLEL_CONJ
|
|
#endif // ! 0
|
|
|
|
#ifdef MR_BOEHM_GC
|
|
if (MR_gc_disable) {
|
|
GC_disable();
|
|
}
|
|
#endif
|
|
|
|
if (MR_memdebug) {
|
|
MR_debug_memory(stderr);
|
|
}
|
|
|
|
// Initialize profiling.
|
|
|
|
#if defined(MR_MPROF_PROFILE_TIME) || defined(MR_MPROF_PROFILE_CALLS) \
|
|
|| defined(MR_MPROF_PROFILE_MEMORY)
|
|
if (MR_profiling) {
|
|
MR_prof_init();
|
|
}
|
|
#endif
|
|
|
|
#ifdef MR_DEEP_PROFILING_TIMING
|
|
if (MR_deep_profiling_save_results) {
|
|
MR_deep_prof_init();
|
|
MR_deep_prof_turn_on_time_profiling();
|
|
}
|
|
|
|
#ifdef MR_DEEP_PROFILING_LOG
|
|
if (MR_deep_prof_log_file != NULL) {
|
|
MR_deep_log_proc_statics(MR_deep_prof_log_file);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MR_RECORD_TERM_SIZES
|
|
if (MR_complexity_save_results) {
|
|
MR_do_init_modules_complexity();
|
|
MR_check_complexity_init();
|
|
}
|
|
#endif
|
|
|
|
// We need to call MR_save_registers(), since we are 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
|
|
// MR_restore_transient_registers(), since we've just returned
|
|
// from a C call.
|
|
|
|
MR_restore_transient_registers();
|
|
MR_save_registers();
|
|
|
|
MR_trace_init();
|
|
|
|
// Initialize the Mercury library.
|
|
(*MR_library_initializer)();
|
|
|
|
// Run any user-defined initialisation predicates.
|
|
(*MR_address_of_init_modules_required)();
|
|
|
|
// Copy the stuff we have set up in registers, stacks etc to the
|
|
// current context of the engine.
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_save_context(&(MR_ENGINE(MR_eng_context)));
|
|
#endif
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_OWN_STACKS
|
|
MR_ENGINE(MR_eng_main_context) = MR_ENGINE(MR_eng_this_context);
|
|
#endif
|
|
|
|
// Now the real tracing starts; undo any updates to the trace state
|
|
// made by the trace code in the library initializer.
|
|
|
|
MR_debug_enabled = saved_debug_enabled;
|
|
MR_trace_count_enabled = saved_trace_count_enabled;
|
|
MR_update_trace_func_enabled();
|
|
MR_trace_start(MR_debug_enabled);
|
|
|
|
if (MR_debug_enabled) {
|
|
MR_selected_trace_func_ptr = MR_exec_trace_func_ptr;
|
|
// MR_debug_enabled overrides MR_trace_count_enabled
|
|
MR_trace_count_enabled = MR_FALSE;
|
|
} else if (MR_trace_count_enabled) {
|
|
MR_register_module_layout = MR_insert_module_info_into_module_table;
|
|
MR_selected_trace_func_ptr = MR_trace_count;
|
|
// Even if the program terminates with an exception,
|
|
// we still want the trace count file to be written out.
|
|
|
|
MR_register_exception_cleanup(MR_trace_record_label_exec_counts, NULL);
|
|
}
|
|
|
|
// Restore the callee-save registers before returning,
|
|
// since they may be used by the C code that called us.
|
|
|
|
MR_restore_regs_from_mem(c_regs);
|
|
|
|
} // end mercury_runtime_init()
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
|
|
// Boehm will call this callback when it runs out of memory, We print an error
|
|
// and abort. Our error is printed after Boehm GC's on error, so we don't need
|
|
// to say much.
|
|
|
|
#ifdef MR_BOEHM_GC
|
|
static void * GC_CALLBACK MR_oom_func(size_t bytes)
|
|
{
|
|
MR_fatal_error("Could not allocate %d bytes, exiting.\n", bytes);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
MR_init_conservative_GC(void)
|
|
{
|
|
#if defined(MR_HGC)
|
|
|
|
MR_hgc_init();
|
|
MR_runqueue_head = NULL;
|
|
MR_hgc_add_root(&MR_runqueue_head);
|
|
(*MR_address_of_init_gc)();
|
|
|
|
#elif defined(MR_BOEHM_GC)
|
|
|
|
// Sometimes Mercury apps fail the GC_is_visible() test.
|
|
// dyn_load.c traverses the entire address space and registers
|
|
// all segments that could possibly have been written to, which
|
|
// makes us suspect that &MR_runqueue_head is not in the registered roots.
|
|
// So we force a write to that address, which seems to make the problem
|
|
// go away.
|
|
|
|
MR_runqueue_head = NULL;
|
|
|
|
// 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.)
|
|
|
|
(*MR_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.
|
|
//
|
|
// With MR_RECORD_TERM_SIZES, we not only add tags in the bottom
|
|
// MR_LOW_TAG_BITS bits of the word, we add the tag to a pointer
|
|
// not just to the first MR_Word in the block, but also to a pointer
|
|
// to the second MR_Word.
|
|
|
|
{
|
|
int i;
|
|
int limit;
|
|
|
|
limit = (1 << MR_LOW_TAG_BITS);
|
|
|
|
#if defined(MR_RECORD_TERM_SIZES) || \
|
|
defined(MR_MPROF_PROFILE_MEMORY_ATTRIBUTION)
|
|
limit += sizeof(MR_Word);
|
|
#endif
|
|
|
|
for (i = 1; i < limit; i++) {
|
|
GC_REGISTER_DISPLACEMENT(i);
|
|
}
|
|
}
|
|
|
|
// Note that the code of several functions in mercury_memory.c
|
|
// depends on boehm_gc's out-of-memory handler to avoid explicitly
|
|
// checking the return values of calls to the GC_MALLOCx family for NULL.
|
|
GC_set_oom_fn(MR_oom_func);
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
// Explicitly put the GC into multithreaded mode. This also launches any
|
|
// parallel marker threads. Without this, parallel marker threads will not
|
|
// be started until the first user thread is created.
|
|
GC_allow_register_threads();
|
|
#endif // MR_THREAD_SAFE
|
|
|
|
#else
|
|
|
|
MR_fatal_error("conservative gc is neither boehm_gc nor hgc");
|
|
|
|
#endif // MR_HGC + MR_BOEHM_GC
|
|
}
|
|
#endif // MR_CONSERVATIVE_GC
|
|
|
|
void
|
|
MR_do_init_modules(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
if (! done) {
|
|
(*MR_address_of_init_modules)();
|
|
MR_close_prof_decl_file();
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_do_init_modules_type_tables(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
if (! done) {
|
|
(*MR_address_of_init_modules_type_tables)();
|
|
done = MR_TRUE;
|
|
|
|
// Some system-defined types have the code to register
|
|
// their type_ctor_infos in the initialization function
|
|
// invoked by MR_do_init_modules.
|
|
|
|
MR_do_init_modules();
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_do_init_modules_debugger(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
if (! done) {
|
|
(*MR_address_of_init_modules_debugger)();
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef MR_RECORD_TERM_SIZES
|
|
void
|
|
MR_do_init_modules_complexity(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
if (! done) {
|
|
(*MR_address_of_init_modules_complexity)();
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Given a string, parse it into arguments and create an argv vector for it.
|
|
// The return value is NULL if the string parses OK, or an error message
|
|
// if it didn't (e.g. if it contained an unterminated quoted string).
|
|
// Also returns args, argv, and argc. It is the caller's responsibility to
|
|
// MR_GC_free() args and argv when they are no longer needed.
|
|
|
|
const char *
|
|
MR_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') {
|
|
*args_ptr = NULL;
|
|
*argv_ptr = NULL;
|
|
*argc_ptr = argc;
|
|
return "unterminated quoted string";
|
|
}
|
|
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++;
|
|
}
|
|
|
|
// Allocate the space.
|
|
|
|
args = MR_GC_NEW_ARRAY(char, args_len);
|
|
argv = MR_GC_NEW_ARRAY(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';
|
|
}
|
|
|
|
*args_ptr = args;
|
|
*argv_ptr = argv;
|
|
*argc_ptr = argc;
|
|
return NULL; // Success.
|
|
}
|
|
|
|
// MR_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
|
|
MR_process_args(int argc, char **argv)
|
|
{
|
|
// It is possible that argc == 0 and argv[0] == NULL on some operating
|
|
// systems. Since that is not actually useful, we ensure that MR_progname
|
|
// is always set to a valid string so that uses of MR_progname do not need
|
|
// to check if it is NULL.
|
|
|
|
if (argc >= 1) {
|
|
MR_progname = argv[0];
|
|
mercury_argc = argc - 1;
|
|
mercury_argv = argv + 1;
|
|
} else {
|
|
MR_progname = NULL;
|
|
mercury_argc = 0;
|
|
mercury_argv = argv;
|
|
}
|
|
|
|
if (MR_progname == NULL) {
|
|
MR_progname = "";
|
|
MR_progname_is_known = MR_FALSE;
|
|
} else {
|
|
MR_progname_is_known = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
// MR_process_environment_options() is a function to parse the options put
|
|
// into MR_runtime_flags by mkinit, the MERCURY_OPTIONS environment variable,
|
|
// and the MERCURY_OPTIONS_progname environment variable.
|
|
|
|
#define MERCURY_OPTIONS "MERCURY_OPTIONS"
|
|
#define WHERE_BUF_SIZE 1000
|
|
|
|
static void
|
|
MR_process_environment_options(void)
|
|
{
|
|
char *gen_env_options;
|
|
char *prog_env_options;
|
|
char *prog_env_option_name;
|
|
int prog_env_option_name_len;
|
|
int mercury_options_len;
|
|
const char *progname;
|
|
const char *s;
|
|
|
|
gen_env_options = getenv(MERCURY_OPTIONS);
|
|
if (gen_env_options == NULL) {
|
|
gen_env_options = (char *) "";
|
|
}
|
|
|
|
// Find out the program's name, stripping off any directory names and the
|
|
// .exe extension on those systems that use it.
|
|
progname = MR_get_program_basename(MR_progname);
|
|
|
|
// Build the program-specific option's name: MERCURY_OPTIONS_progname.
|
|
mercury_options_len = strlen(MERCURY_OPTIONS);
|
|
prog_env_option_name_len = mercury_options_len + 1 + strlen(progname) + 1;
|
|
prog_env_option_name = MR_GC_NEW_ARRAY(char, prog_env_option_name_len);
|
|
strcpy(prog_env_option_name, MERCURY_OPTIONS);
|
|
prog_env_option_name[mercury_options_len] = '_';
|
|
strcpy(prog_env_option_name + mercury_options_len + 1, progname);
|
|
|
|
prog_env_options = getenv(prog_env_option_name);
|
|
if (prog_env_options == NULL) {
|
|
prog_env_options = (char *) "";
|
|
}
|
|
|
|
if (gen_env_options[0] != '\0' || prog_env_options[0] != '\0'
|
|
|| MR_runtime_flags[0] != '\0')
|
|
{
|
|
const char *dummy_cmd;
|
|
int dummy_cmd_len;
|
|
char *dummy_command_line;
|
|
int dummy_command_line_len;
|
|
char *option_arg_str;
|
|
char **option_argv;
|
|
int option_argc;
|
|
int runtime_flags_len;
|
|
int gen_env_options_len;
|
|
int prog_env_options_len;
|
|
int next_slot;
|
|
const char *error_msg;
|
|
|
|
// 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 MR_make_argv()
|
|
// and then to getopt().
|
|
|
|
dummy_cmd = "mercury_runtime";
|
|
dummy_cmd_len = strlen(dummy_cmd);
|
|
runtime_flags_len = strlen(MR_runtime_flags);
|
|
gen_env_options_len = strlen(gen_env_options);
|
|
prog_env_options_len = strlen(prog_env_options);
|
|
dummy_command_line_len =
|
|
dummy_cmd_len + 1 +
|
|
runtime_flags_len + 1 +
|
|
gen_env_options_len + 1 +
|
|
prog_env_options_len + 1;
|
|
dummy_command_line = MR_GC_NEW_ARRAY(char, dummy_command_line_len);
|
|
|
|
next_slot = 0;
|
|
|
|
strcpy(dummy_command_line + next_slot, dummy_cmd);
|
|
next_slot += dummy_cmd_len;
|
|
dummy_command_line[next_slot] = ' ';
|
|
next_slot += 1;
|
|
strcpy(dummy_command_line + next_slot, MR_runtime_flags);
|
|
next_slot += runtime_flags_len;
|
|
dummy_command_line[next_slot] = ' ';
|
|
next_slot += 1;
|
|
strcpy(dummy_command_line + next_slot, gen_env_options);
|
|
next_slot += gen_env_options_len;
|
|
dummy_command_line[next_slot] = ' ';
|
|
next_slot += 1;
|
|
strcpy(dummy_command_line + next_slot, prog_env_options);
|
|
next_slot += prog_env_options_len;
|
|
dummy_command_line[next_slot] = '\0';
|
|
next_slot += 1;
|
|
|
|
// Sanity check.
|
|
if (next_slot != dummy_command_line_len) {
|
|
MR_fatal_error("next_slot != dummy_command_line_len");
|
|
}
|
|
|
|
#ifdef MR_DEBUG_ARGUMENT_HANDLING
|
|
// Enable this is if you need to debug this code.
|
|
printf("progname = <%s>\n", progname);
|
|
printf("MR_runtime_flags = <%s>\n", MR_runtime_flags);
|
|
printf("gen_env_options = <%s>\n", gen_env_options);
|
|
printf("prog_env_options = <%s>\n", prog_env_options);
|
|
printf("dummy_command_line = <%s>\n", dummy_command_line);
|
|
#endif
|
|
|
|
error_msg = MR_make_argv(dummy_command_line, &option_arg_str,
|
|
&option_argv, &option_argc);
|
|
if (error_msg != NULL) {
|
|
char *where_buf;
|
|
int where_buf_cur;
|
|
|
|
where_buf = MR_GC_NEW_ARRAY(char, WHERE_BUF_SIZE);
|
|
where_buf[0] = '\0';
|
|
where_buf_cur = 0;
|
|
|
|
if (gen_env_options[0] != '\0') {
|
|
MR_snprintf(where_buf, WHERE_BUF_SIZE,
|
|
"the %s environment variable", MERCURY_OPTIONS);
|
|
}
|
|
|
|
if (prog_env_options[0] != '\0') {
|
|
where_buf_cur = strlen(where_buf);
|
|
MR_snprintf(where_buf + where_buf_cur,
|
|
WHERE_BUF_SIZE - where_buf_cur,
|
|
"%sthe %s environment variable",
|
|
where_buf_cur == 0 ? "" : " and/or ",
|
|
prog_env_option_name);
|
|
}
|
|
|
|
if (MR_runtime_flags[0] != '\0') {
|
|
where_buf_cur = strlen(where_buf);
|
|
MR_snprintf(where_buf + where_buf_cur,
|
|
WHERE_BUF_SIZE - where_buf_cur,
|
|
"%sthe runtime options built into the executable",
|
|
where_buf_cur == 0 ? "" : " and/or ");
|
|
}
|
|
|
|
MR_fatal_error("error parsing %s:\n%s\n", where_buf, error_msg);
|
|
}
|
|
|
|
MR_GC_free(dummy_command_line);
|
|
MR_process_options(option_argc, option_argv);
|
|
MR_GC_free(option_arg_str);
|
|
MR_GC_free(option_argv);
|
|
}
|
|
|
|
MR_GC_free(prog_env_option_name);
|
|
}
|
|
|
|
enum MR_long_option {
|
|
MR_HEAP_SIZE = 256,
|
|
MR_HEAP_SIZE_KWORDS,
|
|
MR_DETSTACK_SIZE,
|
|
MR_DETSTACK_SIZE_KWORDS,
|
|
MR_NONDETSTACK_SIZE,
|
|
MR_NONDETSTACK_SIZE_KWORDS,
|
|
MR_SMALL_DETSTACK_SIZE,
|
|
MR_SMALL_DETSTACK_SIZE_KWORDS,
|
|
MR_SMALL_NONDETSTACK_SIZE,
|
|
MR_SMALL_NONDETSTACK_SIZE_KWORDS,
|
|
MR_SOLUTIONS_HEAP_SIZE,
|
|
MR_SOLUTIONS_HEAP_SIZE_KWORDS,
|
|
MR_TRAIL_SIZE,
|
|
MR_TRAIL_SIZE_KWORDS,
|
|
MR_TRAIL_SEGMENT_SIZE,
|
|
MR_TRAIL_SEGMENT_SIZE_KWORDS,
|
|
MR_HEAP_REDZONE_SIZE,
|
|
MR_HEAP_REDZONE_SIZE_KWORDS,
|
|
MR_DETSTACK_REDZONE_SIZE,
|
|
MR_DETSTACK_REDZONE_SIZE_KWORDS,
|
|
MR_NONDETSTACK_REDZONE_SIZE,
|
|
MR_NONDETSTACK_REDZONE_SIZE_KWORDS,
|
|
MR_SOLUTIONS_HEAP_REDZONE_SIZE,
|
|
MR_SOLUTIONS_HEAP_REDZONE_SIZE_KWORDS,
|
|
MR_TRAIL_REDZONE_SIZE,
|
|
MR_TRAIL_REDZONE_SIZE_KWORDS,
|
|
MR_HEAP_MARGIN_SIZE,
|
|
MR_HEAP_MARGIN_SIZE_KWORDS,
|
|
MR_HEAP_EXPANSION_FACTOR,
|
|
MR_GENSTACK_SIZE,
|
|
MR_GENSTACK_SIZE_KWORDS,
|
|
MR_CUTSTACK_SIZE,
|
|
MR_CUTSTACK_SIZE_KWORDS,
|
|
MR_PNEGSTACK_SIZE,
|
|
MR_PNEGSTACK_SIZE_KWORDS,
|
|
MR_GEN_DETSTACK_SIZE,
|
|
MR_GEN_DETSTACK_SIZE_KWORDS,
|
|
MR_GEN_NONDETSTACK_SIZE,
|
|
MR_GEN_NONDETSTACK_SIZE_KWORDS,
|
|
MR_GEN_DETSTACK_REDZONE_SIZE,
|
|
MR_GEN_DETSTACK_REDZONE_SIZE_KWORDS,
|
|
MR_GEN_NONDETSTACK_REDZONE_SIZE,
|
|
MR_GEN_NONDETSTACK_REDZONE_SIZE_KWORDS,
|
|
MR_MAX_ENGINES,
|
|
MR_MAX_CONTEXTS_PER_THREAD,
|
|
MR_NUM_CONTEXTS_PER_LC_PER_THREAD,
|
|
MR_RUNTIME_GRANULAITY_WSDEQUE_LENGTH_FACTOR,
|
|
MR_WORKSTEAL_MAX_ATTEMPTS,
|
|
MR_WORKSTEAL_SLEEP_MSECS,
|
|
MR_THREAD_PINNING,
|
|
MR_PROFILE_PARALLEL_EXECUTION,
|
|
MR_THREADSCOPE_USE_TSC,
|
|
MR_MDB_TTY,
|
|
MR_MDB_IN,
|
|
MR_MDB_OUT,
|
|
MR_MDB_ERR,
|
|
MR_MDB_DISABLE_PROGRESS,
|
|
MR_MDB_BENCHMARK_SILENT,
|
|
MR_MDB_IN_WINDOW,
|
|
MR_FORCE_READLINE,
|
|
MR_NUM_OUTPUT_ARGS,
|
|
MR_DEBUG_THREADS_OPT,
|
|
MR_DEEP_PROF_DEBUG_FILE_OPT,
|
|
MR_DEEP_PROF_RANDOM_WRITE,
|
|
MR_DEEP_PROF_STD_NAME,
|
|
MR_DEEP_PROF_LOG_FILE_OPT,
|
|
MR_DEEP_PROF_LOG_PROG_OPT,
|
|
MR_TABLING_STATISTICS_OPT,
|
|
MR_TRACE_COUNT_OPT,
|
|
MR_TRACE_COUNT_IF_EXEC_OPT,
|
|
MR_TRACE_COUNT_SUMMARY_FILE_OPT,
|
|
MR_TRACE_COUNT_SUMMARY_CMD_OPT,
|
|
MR_TRACE_COUNT_SUMMARY_MAX_OPT,
|
|
MR_COVERAGE_TEST_OPT,
|
|
MR_COVERAGE_TEST_IF_EXEC_OPT,
|
|
MR_TRACE_COUNT_FILE,
|
|
MR_MEM_USAGE_REPORT,
|
|
MR_BOEHM_GC_FREE_SPACE_DIVISOR,
|
|
MR_BOEHM_GC_CALC_TIME,
|
|
MR_BOEHM_WRITE_SIZE_MAP,
|
|
MR_FP_ROUNDING_MODE
|
|
};
|
|
|
|
struct MR_option MR_long_opts[] = {
|
|
{ "heap-size", 1, 0, MR_HEAP_SIZE },
|
|
{ "heap-size-kwords", 1, 0, MR_HEAP_SIZE_KWORDS },
|
|
{ "detstack-size", 1, 0, MR_DETSTACK_SIZE },
|
|
{ "det-stack-size", 1, 0, MR_DETSTACK_SIZE },
|
|
{ "detstack-size-kwords", 1, 0, MR_DETSTACK_SIZE_KWORDS },
|
|
{ "det-stack-size-kwords", 1, 0, MR_DETSTACK_SIZE_KWORDS },
|
|
{ "nondetstack-size", 1, 0, MR_NONDETSTACK_SIZE },
|
|
{ "nondet-stack-size", 1, 0, MR_NONDETSTACK_SIZE },
|
|
{ "nondetstack-size-kwords", 1, 0, MR_NONDETSTACK_SIZE_KWORDS },
|
|
{ "nondet-stack-size-kwords", 1, 0, MR_NONDETSTACK_SIZE_KWORDS },
|
|
{ "small-detstack-size", 1, 0, MR_SMALL_DETSTACK_SIZE },
|
|
{ "small-det-stack-size", 1, 0, MR_SMALL_DETSTACK_SIZE },
|
|
{ "small-detstack-size-kwords", 1, 0, MR_SMALL_DETSTACK_SIZE_KWORDS },
|
|
{ "small-det-stack-size-kwords", 1, 0, MR_SMALL_DETSTACK_SIZE_KWORDS },
|
|
{ "small-nondetstack-size", 1, 0, MR_SMALL_NONDETSTACK_SIZE },
|
|
{ "small-nondet-stack-size", 1, 0, MR_SMALL_NONDETSTACK_SIZE },
|
|
{ "small-nondetstack-size-kwords",
|
|
1, 0, MR_SMALL_NONDETSTACK_SIZE_KWORDS },
|
|
{ "small-nondet-stack-size-kwords",
|
|
1, 0, MR_SMALL_NONDETSTACK_SIZE_KWORDS },
|
|
{ "solutions-heap-size", 1, 0, MR_SOLUTIONS_HEAP_SIZE },
|
|
{ "solutions-heap-size-kwords", 1, 0, MR_SOLUTIONS_HEAP_SIZE_KWORDS },
|
|
{ "trail-size", 1, 0, MR_TRAIL_SIZE },
|
|
{ "trail-size-kwords", 1, 0, MR_TRAIL_SIZE_KWORDS },
|
|
{ "trail-segment-size", 1, 0, MR_TRAIL_SEGMENT_SIZE },
|
|
{ "trail-segment-size-kwords", 1, 0, MR_TRAIL_SEGMENT_SIZE_KWORDS },
|
|
{ "heap-redzone-size", 1, 0, MR_HEAP_REDZONE_SIZE },
|
|
{ "heap-redzone-size-kwords", 1, 0, MR_HEAP_REDZONE_SIZE_KWORDS },
|
|
{ "detstack-redzone-size", 1, 0, MR_DETSTACK_REDZONE_SIZE },
|
|
{ "det-stack-redzone-size", 1, 0, MR_DETSTACK_REDZONE_SIZE },
|
|
{ "detstack-redzone-size-kwords",
|
|
1, 0, MR_DETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "det-stack-redzone-size-kwords",
|
|
1, 0, MR_DETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "nondetstack-redzone-size", 1, 0, MR_NONDETSTACK_REDZONE_SIZE },
|
|
{ "nondet-stack-redzone-size", 1, 0, MR_NONDETSTACK_REDZONE_SIZE },
|
|
{ "nondetstack-redzone-size-kwords",
|
|
1, 0, MR_NONDETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "nondet-stack-redzone-size-kwords",
|
|
1, 0, MR_NONDETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "solutions-heap-redzone-size",1, 0, MR_SOLUTIONS_HEAP_REDZONE_SIZE },
|
|
{ "solutions-heap-redzone-size-kwords",
|
|
1, 0, MR_SOLUTIONS_HEAP_REDZONE_SIZE_KWORDS },
|
|
{ "trail-redzone-size", 1, 0, MR_TRAIL_REDZONE_SIZE },
|
|
{ "trail-redzone-size-kwords", 1, 0, MR_TRAIL_REDZONE_SIZE_KWORDS },
|
|
{ "heap-margin-size", 1, 0, MR_HEAP_MARGIN_SIZE },
|
|
{ "heap-margin-size-kwords", 1, 0, MR_HEAP_MARGIN_SIZE_KWORDS },
|
|
{ "heap-expansion-factor", 1, 0, MR_HEAP_EXPANSION_FACTOR },
|
|
{ "genstack-size", 1, 0, MR_GENSTACK_SIZE },
|
|
{ "genstack-size-kwords", 1, 0, MR_GENSTACK_SIZE_KWORDS },
|
|
{ "cutstack-size", 1, 0, MR_CUTSTACK_SIZE },
|
|
{ "cutstack-size-kwords", 1, 0, MR_CUTSTACK_SIZE_KWORDS },
|
|
{ "pnegstack-size", 1, 0, MR_PNEGSTACK_SIZE },
|
|
{ "pnegstack-size-kwords", 1, 0, MR_PNEGSTACK_SIZE_KWORDS },
|
|
{ "gen-detstack-size", 1, 0, MR_GEN_DETSTACK_SIZE },
|
|
{ "gen-detstack-size-kwords", 1, 0, MR_GEN_DETSTACK_SIZE_KWORDS },
|
|
{ "gen-nondetstack-size", 1, 0, MR_GEN_NONDETSTACK_SIZE },
|
|
{ "gen-nondetstack-size-kwords", 1, 0, MR_GEN_NONDETSTACK_SIZE_KWORDS },
|
|
{ "gen-detstack-zone-size", 1, 0, MR_GEN_DETSTACK_REDZONE_SIZE },
|
|
{ "gen-detstack-zone-size-kwords",
|
|
1, 0, MR_GEN_DETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "gen-nondetstack-zone-size",
|
|
1, 0, MR_GEN_NONDETSTACK_REDZONE_SIZE },
|
|
{ "gen-nondetstack-zone-size-kwords",
|
|
1, 0, MR_GEN_NONDETSTACK_REDZONE_SIZE_KWORDS },
|
|
{ "max-engines", 1, 0, MR_MAX_ENGINES },
|
|
{ "max-contexts-per-thread", 1, 0, MR_MAX_CONTEXTS_PER_THREAD },
|
|
{ "num-contexts-per-lc-per-thread",
|
|
1, 0, MR_NUM_CONTEXTS_PER_LC_PER_THREAD },
|
|
{ "runtime-granularity-wsdeque-length-factor",
|
|
1, 0, MR_RUNTIME_GRANULAITY_WSDEQUE_LENGTH_FACTOR },
|
|
{ "thread-pinning", 0, 0, MR_THREAD_PINNING },
|
|
{ "profile-parallel-execution", 0, 0, MR_PROFILE_PARALLEL_EXECUTION },
|
|
{ "threadscope-use-tsc", 0, 0, MR_THREADSCOPE_USE_TSC },
|
|
{ "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 },
|
|
{ "mdb-in-window", 0, 0, MR_MDB_IN_WINDOW },
|
|
{ "mdb-disable-progress", 0, 0, MR_MDB_DISABLE_PROGRESS },
|
|
{ "mdb-benchmark-silent", 0, 0, MR_MDB_BENCHMARK_SILENT },
|
|
{ "force-readline", 0, 0, MR_FORCE_READLINE },
|
|
{ "num-output-args", 1, 0, MR_NUM_OUTPUT_ARGS },
|
|
{ "debug-threads", 0, 0, MR_DEBUG_THREADS_OPT },
|
|
{ "deep-debug-file", 0, 0, MR_DEEP_PROF_DEBUG_FILE_OPT },
|
|
// The --deep-random-write option is only for use by tools/bootcheck.
|
|
// It is deliberately not documented.
|
|
{ "deep-random-write", 1, 0, MR_DEEP_PROF_RANDOM_WRITE },
|
|
{ "deep-std-name", 0, 0, MR_DEEP_PROF_STD_NAME },
|
|
{ "deep-log-file", 1, 0, MR_DEEP_PROF_LOG_FILE_OPT },
|
|
{ "deep-log-prog", 1, 0, MR_DEEP_PROF_LOG_PROG_OPT },
|
|
{ "tabling-statistics", 0, 0, MR_TABLING_STATISTICS_OPT },
|
|
{ "trace-count", 0, 0, MR_TRACE_COUNT_OPT },
|
|
{ "trace-count-if-exec", 1, 0, MR_TRACE_COUNT_IF_EXEC_OPT },
|
|
{ "coverage-test", 0, 0, MR_COVERAGE_TEST_OPT },
|
|
{ "coverage-test-if-exec", 1, 0, MR_COVERAGE_TEST_IF_EXEC_OPT },
|
|
{ "tc-output-file", 1, 0, MR_TRACE_COUNT_FILE },
|
|
{ "trace-count-output-file", 1, 0, MR_TRACE_COUNT_FILE },
|
|
{ "tc-summary-file",
|
|
1, 0, MR_TRACE_COUNT_SUMMARY_FILE_OPT },
|
|
{ "trace-count-summary-file",
|
|
1, 0, MR_TRACE_COUNT_SUMMARY_FILE_OPT },
|
|
{ "tc-summary-cmd", 1, 0, MR_TRACE_COUNT_SUMMARY_CMD_OPT },
|
|
{ "trace-count-summary-cmd", 1, 0, MR_TRACE_COUNT_SUMMARY_CMD_OPT },
|
|
{ "tc-summary-max", 1, 0, MR_TRACE_COUNT_SUMMARY_MAX_OPT },
|
|
{ "trace-count-summary-max", 1, 0, MR_TRACE_COUNT_SUMMARY_MAX_OPT },
|
|
{ "mem-usage-report", 1, 0, MR_MEM_USAGE_REPORT },
|
|
{ "boehm-gc-free-space-divisor", 1, 0, MR_BOEHM_GC_FREE_SPACE_DIVISOR },
|
|
{ "boehm-gc-calc-time", 0, 0, MR_BOEHM_GC_CALC_TIME },
|
|
{ "boehm-write-size-map", 0, 0, MR_BOEHM_WRITE_SIZE_MAP },
|
|
{ "fp-rounding-mode", 1, 0, MR_FP_ROUNDING_MODE },
|
|
|
|
// This needs to be kept at the end.
|
|
{ NULL, 0, 0, 0 }
|
|
};
|
|
|
|
|
|
static void
|
|
MR_process_options(int argc, char **argv)
|
|
{
|
|
unsigned long size;
|
|
int c;
|
|
int long_index;
|
|
|
|
while ((c = MR_getopt_long(argc, argv, "cC:d:D:e:i:m:n:o:pP:sST:xX",
|
|
MR_long_opts, &long_index)) != EOF)
|
|
{
|
|
switch (c)
|
|
{
|
|
case MR_HEAP_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_size = size;
|
|
break;
|
|
|
|
case MR_HEAP_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_DETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_detstack_size = size;
|
|
break;
|
|
|
|
case MR_DETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_detstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_NONDETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_nondetstack_size = size;
|
|
break;
|
|
|
|
case MR_NONDETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_nondetstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_SMALL_DETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#ifndef MR_STACK_SEGMENTS
|
|
MR_small_detstack_size = size;
|
|
#endif
|
|
break;
|
|
|
|
case MR_SMALL_DETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#ifndef MR_STACK_SEGMENTS
|
|
MR_small_detstack_size = size * sizeof(MR_Word);
|
|
#endif
|
|
break;
|
|
|
|
case MR_SMALL_NONDETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#ifndef MR_STACK_SEGMENTS
|
|
MR_small_nondetstack_size = size;
|
|
#endif
|
|
break;
|
|
|
|
case MR_SMALL_NONDETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
#ifndef MR_STACK_SEGMENTS
|
|
MR_small_nondetstack_size = size * sizeof(MR_Word);
|
|
#endif
|
|
break;
|
|
|
|
case MR_SOLUTIONS_HEAP_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_solutions_heap_size = size;
|
|
break;
|
|
|
|
case MR_SOLUTIONS_HEAP_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_solutions_heap_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_TRAIL_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#if defined(MR_USE_FIXED_SIZE_TRAIL)
|
|
MR_trail_size = size;
|
|
#endif
|
|
break;
|
|
|
|
case MR_TRAIL_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#if defined(MR_USE_FIXED_SIZE_TRAIL)
|
|
MR_trail_size = size * sizeof(MR_Word);
|
|
#endif
|
|
break;
|
|
|
|
case MR_TRAIL_SEGMENT_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#if !defined(MR_USE_FIXED_SIZE_TRAIL)
|
|
MR_trail_size = size;
|
|
#endif
|
|
break;
|
|
|
|
case MR_TRAIL_SEGMENT_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#if !defined(MR_USE_FIXED_SIZE_TRAIL)
|
|
MR_trail_size = size * sizeof(MR_Word);
|
|
#endif
|
|
break;
|
|
|
|
case MR_HEAP_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_zone_size = size;
|
|
break;
|
|
|
|
case MR_HEAP_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_DETSTACK_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_detstack_zone_size = size;
|
|
break;
|
|
|
|
case MR_DETSTACK_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_detstack_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_NONDETSTACK_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_nondetstack_zone_size = size;
|
|
break;
|
|
|
|
case MR_NONDETSTACK_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_nondetstack_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_SOLUTIONS_HEAP_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_solutions_heap_zone_size = size;
|
|
break;
|
|
|
|
case MR_SOLUTIONS_HEAP_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_solutions_heap_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_TRAIL_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_trail_zone_size = size;
|
|
break;
|
|
|
|
case MR_TRAIL_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_trail_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_HEAP_MARGIN_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_margin_size = size;
|
|
break;
|
|
|
|
case MR_HEAP_MARGIN_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_heap_margin_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_HEAP_EXPANSION_FACTOR:
|
|
if (sscanf(MR_optarg, "%lf", &MR_heap_expansion_factor) != 1) {
|
|
MR_usage();
|
|
}
|
|
break;
|
|
|
|
case MR_GENSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_genstack_size = size;
|
|
break;
|
|
|
|
case MR_GENSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_genstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_CUTSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_cutstack_size = size;
|
|
break;
|
|
|
|
case MR_CUTSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_cutstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_PNEGSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_pnegstack_size = size;
|
|
break;
|
|
|
|
case MR_PNEGSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_pnegstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_GEN_DETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_detstack_size = size;
|
|
break;
|
|
|
|
case MR_GEN_DETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_detstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_GEN_NONDETSTACK_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_nondetstack_size = size;
|
|
break;
|
|
|
|
case MR_GEN_NONDETSTACK_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_nondetstack_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_GEN_DETSTACK_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_detstack_zone_size = size;
|
|
break;
|
|
|
|
case MR_GEN_DETSTACK_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_detstack_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_GEN_NONDETSTACK_REDZONE_SIZE:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_nondetstack_zone_size = size;
|
|
break;
|
|
|
|
case MR_GEN_NONDETSTACK_REDZONE_SIZE_KWORDS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_gen_nondetstack_zone_size = size * sizeof(MR_Word);
|
|
break;
|
|
|
|
case MR_MAX_ENGINES:
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
if (size < 1) {
|
|
MR_usage();
|
|
}
|
|
MR_max_engines = MR_min(size, MR_ENGINE_ID_NONE);
|
|
#endif
|
|
break;
|
|
|
|
case MR_MAX_CONTEXTS_PER_THREAD:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_max_contexts_per_thread = size;
|
|
break;
|
|
|
|
case MR_NUM_CONTEXTS_PER_LC_PER_THREAD:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_num_contexts_per_loop_control_per_thread = size;
|
|
break;
|
|
|
|
case MR_RUNTIME_GRANULAITY_WSDEQUE_LENGTH_FACTOR:
|
|
#if defined(MR_LL_PARALLEL_CONJ)
|
|
if (sscanf(MR_optarg, "%"MR_INTEGER_LENGTH_MODIFIER"u",
|
|
&MR_granularity_wsdeque_length_factor) != 1)
|
|
{
|
|
MR_usage();
|
|
}
|
|
if (MR_granularity_wsdeque_length_factor < 1) {
|
|
MR_usage();
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case MR_THREAD_PINNING:
|
|
#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_THREAD_PINNING)
|
|
MR_thread_pinning = MR_TRUE;
|
|
#endif
|
|
break;
|
|
|
|
case MR_PROFILE_PARALLEL_EXECUTION:
|
|
#ifdef MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
|
|
MR_profile_parallel_execution = MR_TRUE;
|
|
#endif
|
|
break;
|
|
|
|
case MR_THREADSCOPE_USE_TSC:
|
|
#ifdef MR_THREADSCOPE
|
|
MR_threadscope_use_tsc = MR_TRUE;
|
|
#endif
|
|
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 'n':
|
|
case MR_NUM_OUTPUT_ARGS:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_num_output_args = size;
|
|
break;
|
|
|
|
case 'w':
|
|
case MR_MDB_IN_WINDOW:
|
|
MR_mdb_in_window = MR_TRUE;
|
|
break;
|
|
|
|
case MR_MDB_DISABLE_PROGRESS:
|
|
MR_mdb_decl_print_progress = MR_FALSE;
|
|
break;
|
|
|
|
case MR_MDB_BENCHMARK_SILENT:
|
|
MR_mdb_benchmark_silent = MR_TRUE;
|
|
break;
|
|
|
|
case MR_FORCE_READLINE:
|
|
MR_force_readline = MR_TRUE;
|
|
#if !defined(MR_USE_READLINE) && !defined(MR_USE_EDITLINE)
|
|
printf("Mercury runtime: `--force-readline' is specified "
|
|
"in MERCURY_OPTIONS\n");
|
|
printf("but readline() is not available.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
#endif
|
|
break;
|
|
|
|
case MR_DEBUG_THREADS_OPT:
|
|
#ifdef MR_THREAD_SAFE
|
|
MR_debug_threads = MR_TRUE;
|
|
#endif
|
|
break;
|
|
|
|
case MR_DEEP_PROF_DEBUG_FILE_OPT:
|
|
MR_deep_prof_debug_file_flag = MR_TRUE;
|
|
break;
|
|
|
|
case MR_DEEP_PROF_RANDOM_WRITE:
|
|
if (sscanf(MR_optarg, "%u", &MR_deep_prof_random_write) != 1) {
|
|
MR_usage();
|
|
}
|
|
break;
|
|
|
|
case MR_DEEP_PROF_STD_NAME:
|
|
MR_deep_prof_std_name_flag = MR_TRUE;
|
|
break;
|
|
|
|
case MR_DEEP_PROF_LOG_FILE_OPT:
|
|
#if defined(MR_DEEP_PROFILING) && defined(MR_DEEP_PROFILING_LOG)
|
|
MR_deep_prof_log_file = fopen(MR_optarg, "w");
|
|
if (MR_deep_prof_log_file == NULL) {
|
|
perror(MR_optarg);
|
|
exit(1);
|
|
}
|
|
#else
|
|
printf("Mercury runtime: `--deep-log-file' is specified "
|
|
"in MERCURY_OPTIONS\n");
|
|
printf("but support for it is not enabled.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
#endif
|
|
break;
|
|
|
|
case MR_DEEP_PROF_LOG_PROG_OPT:
|
|
#if defined(MR_DEEP_PROFILING) && defined(MR_DEEP_PROFILING_LOG)
|
|
MR_deep_prof_log_file = popen(MR_optarg, "w");
|
|
if (MR_deep_prof_log_file == NULL) {
|
|
perror(MR_optarg);
|
|
exit(1);
|
|
}
|
|
#else
|
|
printf("Mercury runtime: `--deep-log-prog' is specified "
|
|
"in MERCURY_OPTIONS\n");
|
|
printf("but support for it is not enabled.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
#endif
|
|
break;
|
|
|
|
case MR_TABLING_STATISTICS_OPT:
|
|
MR_print_table_statistics = MR_TRUE;
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_OPT:
|
|
MR_trace_count_enabled = MR_TRUE;
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_IF_EXEC_OPT:
|
|
if (MR_matches_exec_name(MR_optarg)) {
|
|
MR_trace_count_enabled = MR_TRUE;
|
|
}
|
|
break;
|
|
|
|
case MR_COVERAGE_TEST_OPT:
|
|
MR_coverage_test_enabled = MR_TRUE;
|
|
MR_trace_count_enabled = MR_TRUE;
|
|
break;
|
|
|
|
case MR_COVERAGE_TEST_IF_EXEC_OPT:
|
|
if (MR_matches_exec_name(MR_optarg)) {
|
|
MR_coverage_test_enabled = MR_TRUE;
|
|
MR_trace_count_enabled = MR_TRUE;
|
|
}
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_FILE:
|
|
if (MR_trace_count_summary_file != NULL) {
|
|
MR_fatal_error(
|
|
"--trace-count-file and --trace-count-summary-file"
|
|
" are mutually exclusive\n");
|
|
}
|
|
|
|
MR_trace_counts_file = MR_copy_string(MR_optarg);
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_SUMMARY_FILE_OPT:
|
|
if (MR_trace_counts_file != NULL) {
|
|
MR_fatal_error(
|
|
"--trace-count-file and --trace-count-summary-file"
|
|
" are mutually exclusive\n");
|
|
}
|
|
|
|
MR_trace_count_summary_file = MR_copy_string(MR_optarg);
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_SUMMARY_CMD_OPT:
|
|
MR_trace_count_summary_cmd = MR_copy_string(MR_optarg);
|
|
break;
|
|
|
|
case MR_TRACE_COUNT_SUMMARY_MAX_OPT:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
if (size < 2) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_trace_count_summary_max = size;
|
|
break;
|
|
|
|
case MR_MEM_USAGE_REPORT:
|
|
MR_mem_usage_report_prefix = MR_copy_string(MR_optarg);
|
|
break;
|
|
|
|
case MR_BOEHM_GC_FREE_SPACE_DIVISOR:
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
if (size < 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
#ifdef MR_BOEHM_GC
|
|
GC_set_free_space_divisor(size);
|
|
#endif
|
|
break;
|
|
|
|
case MR_BOEHM_GC_CALC_TIME:
|
|
#ifdef MR_BOEHM_GC
|
|
GC_start_performance_measurement();
|
|
#endif
|
|
break;
|
|
|
|
case MR_BOEHM_WRITE_SIZE_MAP:
|
|
#ifdef MR_BOEHM_GC
|
|
fprintf(stdout, "The boehm gc size map:\n");
|
|
GC_mercury_write_size_map(stdout);
|
|
fprintf(stdout, "\n");
|
|
#endif
|
|
break;
|
|
|
|
case MR_FP_ROUNDING_MODE:
|
|
#if defined(MR_HAVE_FENV_H) && defined(MR_HAVE_FESETROUND)
|
|
{
|
|
int rounding_mode;
|
|
|
|
// Particular rounding modes are only supported if the
|
|
// corresponding FE_* macro is defined. The four below are
|
|
// the ones from C99. C99 says that these macros
|
|
// should expand to a nonnegative value, so we use a
|
|
// negative value to indicate that the selected rounding
|
|
// mode is not supported by the system.
|
|
|
|
if (MR_streq(MR_optarg, "downward")) {
|
|
#if defined(FE_DOWNWARD)
|
|
rounding_mode = FE_DOWNWARD;
|
|
#else
|
|
rounding_mode = -1;
|
|
#endif
|
|
} else if (MR_streq(MR_optarg, "upward")) {
|
|
#if defined(FE_UPWARD)
|
|
rounding_mode = FE_UPWARD;
|
|
#else
|
|
rounding_mode = -1;
|
|
#endif
|
|
} else if (MR_streq(MR_optarg, "toward_zero")) {
|
|
#if defined(FE_TOWARDZERO)
|
|
rounding_mode = FE_TOWARDZERO;
|
|
#else
|
|
rounding_mode = -1;
|
|
#endif
|
|
} else if (MR_streq(MR_optarg, "to_nearest")) {
|
|
#if defined(FE_TONEAREST)
|
|
rounding_mode = FE_TONEAREST;
|
|
#else
|
|
rounding_mode = -1;
|
|
#endif
|
|
} else {
|
|
MR_usage();
|
|
}
|
|
|
|
if (rounding_mode < 0) {
|
|
printf("Mercury runtime: the selected rounding mode "
|
|
"is not supported by this system.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
} else {
|
|
if (fesetround(rounding_mode) != 0) {
|
|
printf("Mercury runtime: could not establish "
|
|
"the selected rounding mode.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
printf("Mercury runtime: `--fp-rounding-mode' is specified "
|
|
"in MERCURY_OPTIONS\n");
|
|
printf("but the rounding mode cannot be changed "
|
|
"on this system.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
#endif
|
|
break;
|
|
|
|
case 'c':
|
|
MR_check_space = MR_TRUE;
|
|
break;
|
|
|
|
case 'C':
|
|
if (sscanf(MR_optarg, "%lu", &size) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
MR_pcache_size = size * 1024;
|
|
break;
|
|
|
|
case 'd':
|
|
if (MR_streq(MR_optarg, "a")) {
|
|
MR_calldebug = MR_TRUE;
|
|
MR_nondetstackdebug = MR_TRUE;
|
|
MR_detstackdebug = MR_TRUE;
|
|
MR_heapdebug = MR_TRUE;
|
|
MR_gotodebug = MR_TRUE;
|
|
MR_sregdebug = MR_TRUE;
|
|
MR_finaldebug = MR_TRUE;
|
|
MR_tracedebug = MR_TRUE;
|
|
#ifdef MR_NATIVE_GC
|
|
MR_agc_debug = MR_TRUE;
|
|
#endif
|
|
} else if (MR_streq(MR_optarg, "A")) {
|
|
MR_lld_print_always_enabled = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "b")) {
|
|
MR_nondetstackdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "B")) {
|
|
if (sscanf(MR_optarg+1, "%u", &MR_lld_start_block) != 1) {
|
|
MR_usage();
|
|
}
|
|
// The call count will never rise above zero unless
|
|
// we invoke the low level debugging functions at calls.
|
|
MR_calldebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "c")) {
|
|
MR_calldebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "d")) {
|
|
MR_detaildebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "e")) {
|
|
MR_standardize_event_details = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "f")) {
|
|
MR_finaldebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "g")) {
|
|
MR_gotodebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "G")) {
|
|
#if defined(MR_NATIVE_GC)
|
|
MR_agc_debug = MR_TRUE;
|
|
#else
|
|
// Ignore inapplicable option.
|
|
;
|
|
#endif
|
|
} else if (MR_streq(MR_optarg, "h")) {
|
|
MR_heapdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "H")) {
|
|
MR_hashdebug = MR_TRUE;
|
|
} else if (MR_optarg[0] == 'i') {
|
|
MR_lld_print_more_min_max = strdup(MR_optarg + 1);
|
|
MR_setup_call_intervals(&MR_lld_print_more_min_max,
|
|
&MR_lld_print_min, &MR_lld_print_max);
|
|
// The call count will never rise above zero unless
|
|
// we invoke the low level debugging functions at calls.
|
|
MR_calldebug = MR_TRUE;
|
|
} else if (MR_optarg[0] == 'I') {
|
|
MR_watch_csd_start_name = strdup(MR_optarg+1);
|
|
} else if (MR_optarg[0] == 'j') {
|
|
MR_lld_start_name = strdup(MR_optarg+1);
|
|
} else if (MR_streq(MR_optarg, "l")) {
|
|
MR_printlocndebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "m")) {
|
|
MR_memdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "o")) {
|
|
MR_ordregdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "p")) {
|
|
MR_progdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "P")) {
|
|
MR_calldebug = MR_TRUE;
|
|
MR_gotodebug = MR_TRUE;
|
|
MR_finaldebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "r")) {
|
|
MR_sregdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "R")) {
|
|
MR_anyregdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "s")) {
|
|
MR_detstackdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "S")) {
|
|
MR_tablestackdebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "t")) {
|
|
MR_tracedebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "T")) {
|
|
MR_tabledebug = MR_TRUE;
|
|
} else if (MR_streq(MR_optarg, "u")) {
|
|
MR_unbufdebug = MR_TRUE;
|
|
} else if (MR_optarg[0] == 'w' || MR_optarg[0] == 'W') {
|
|
MR_Integer addr;
|
|
|
|
if (MR_optarg[1] == '0' && MR_optarg[2] == 'x') {
|
|
if (sscanf(MR_optarg+3, "%" MR_INTEGER_LENGTH_MODIFIER "x", &addr) != 1) {
|
|
MR_usage();
|
|
}
|
|
} else {
|
|
if (sscanf(MR_optarg+1, "%" MR_INTEGER_LENGTH_MODIFIER "u", &addr) != 1) {
|
|
MR_usage();
|
|
}
|
|
}
|
|
|
|
MR_anyregdebug = MR_TRUE;
|
|
if (MR_optarg[0] == 'w') {
|
|
MR_watch_addr = (MR_Word *) addr;
|
|
} else {
|
|
MR_watch_csd_addr = (MR_CallSiteDynamic *) addr;
|
|
}
|
|
|
|
// The watch code is called only from the
|
|
// debug messages controlled by MR_calldebug.
|
|
|
|
MR_calldebug = MR_TRUE;
|
|
} else {
|
|
MR_usage();
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
MR_debug_enabled = MR_TRUE;
|
|
MR_debug_ever_enabled = MR_TRUE;
|
|
|
|
if (MR_streq(MR_optarg, "i")) {
|
|
MR_trace_handler = MR_TRACE_INTERNAL;
|
|
#ifdef MR_USE_EXTERNAL_DEBUGGER
|
|
} else if (MR_streq(MR_optarg, "e")) {
|
|
MR_trace_handler = MR_TRACE_EXTERNAL;
|
|
#endif
|
|
} else {
|
|
MR_usage();
|
|
}
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
MR_profiling = MR_FALSE;
|
|
break;
|
|
|
|
case 'P':
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
if (sscanf(MR_optarg, "%"MR_INTEGER_LENGTH_MODIFIER"u",
|
|
&MR_num_ws_engines) != 1) {
|
|
MR_usage();
|
|
}
|
|
|
|
if (MR_num_ws_engines < 1) {
|
|
MR_usage();
|
|
}
|
|
if (MR_num_ws_engines > MR_ENGINE_ID_NONE) {
|
|
MR_num_ws_engines = MR_ENGINE_ID_NONE;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case 's':
|
|
MR_deep_profiling_save_results = MR_FALSE;
|
|
MR_complexity_save_results = MR_FALSE;
|
|
break;
|
|
|
|
case 'S':
|
|
MR_print_deep_profiling_statistics = MR_TRUE;
|
|
break;
|
|
|
|
case 'T':
|
|
if (MR_streq(MR_optarg, "r")) {
|
|
MR_time_profile_method = MR_profile_real_time;
|
|
} else if (MR_streq(MR_optarg, "v")) {
|
|
MR_time_profile_method = MR_profile_user_time;
|
|
} else if (MR_streq(MR_optarg, "p")) {
|
|
MR_time_profile_method = MR_profile_user_plus_system_time;
|
|
} else {
|
|
MR_usage();
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
#ifdef MR_BOEHM_GC
|
|
MR_gc_disable = MR_TRUE;
|
|
#endif
|
|
break;
|
|
|
|
case 'X':
|
|
#ifdef MR_VERIFY_FAKE_REGISTERS
|
|
MR_verify_fake_registers();
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
MR_usage();
|
|
|
|
}
|
|
}
|
|
|
|
if (MR_lld_print_min > 0 || MR_lld_start_name != NULL) {
|
|
MR_lld_print_enabled = MR_FALSE;
|
|
}
|
|
|
|
if (MR_lld_print_always_enabled) {
|
|
MR_lld_print_enabled = MR_TRUE;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#if !defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE) && \
|
|
!defined(MR_STACK_SEGMENTS)
|
|
if (MR_small_detstack_size > MR_detstack_size) {
|
|
printf("The small detstack size must be smaller than the "
|
|
"regular detstack size.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
|
|
if (MR_small_nondetstack_size > MR_nondetstack_size) {
|
|
printf("The small nondetstack size must be smaller than the "
|
|
"regular nondetstack size.\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
MR_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);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_matches_exec_name(const char *option)
|
|
{
|
|
const char *exec_name;
|
|
|
|
exec_name = MR_get_program_basename(MR_progname);
|
|
|
|
if (MR_streq(option, exec_name)) {
|
|
return MR_TRUE;
|
|
} else {
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
// Get the next interval from *more_str_ptr, which should point to a string
|
|
// containing a comma-separated list of integer intervals. The last interval
|
|
// may be open ended.
|
|
|
|
void
|
|
MR_setup_call_intervals(char **more_str_ptr,
|
|
unsigned long *min_ptr, unsigned long *max_ptr)
|
|
{
|
|
char *more_str;
|
|
unsigned long min, max;
|
|
int n;
|
|
|
|
more_str = *more_str_ptr;
|
|
|
|
// Relying on the return value from sscanf() with %n is non-portable,
|
|
// so we need to call sscanf() twice here.
|
|
|
|
if (sscanf(more_str, "%lu-%lu", &min, &max) == 2) {
|
|
sscanf(more_str, "%lu-%lu%n", &min, &max, &n);
|
|
more_str += n;
|
|
if (more_str[0] == ',') {
|
|
more_str++;
|
|
}
|
|
} else if (sscanf(more_str, "%lu-", &min) == 1) {
|
|
more_str = NULL;
|
|
max = (unsigned long) -1;
|
|
} else {
|
|
more_str = NULL;
|
|
min = 0;
|
|
max = (unsigned long) -1;
|
|
}
|
|
|
|
*more_str_ptr = more_str;
|
|
*min_ptr = min;
|
|
*max_ptr = max;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
mercury_runtime_main(void)
|
|
{
|
|
#if MR_NUM_REAL_REGS > 0
|
|
MR_Word c_regs[MR_NUM_REAL_REGS];
|
|
#endif
|
|
|
|
#if defined(MR_DEBUG_THE_RUNTIME) && defined(MR_USE_GCC_NONLOCAL_GOTOS)
|
|
unsigned char safety_buffer[SAFETY_BUFFER_SIZE];
|
|
#endif
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
MR_CallSiteDynList **saved_cur_callback;
|
|
MR_CallSiteDynamic *saved_cur_csd;
|
|
#endif
|
|
|
|
#ifdef MR_MSVC_STRUCTURED_EXCEPTIONS
|
|
// Under Win32 we use the following construction to handle exceptions.
|
|
// __try
|
|
// {
|
|
// <various stuff>
|
|
// }
|
|
// __except(MR_filter_win32_exception(GetExceptionInformation())
|
|
// {
|
|
// }
|
|
//
|
|
// This type of construction allows us to retrieve all the information
|
|
// we need (exception type, address, etc) to display a "meaningful"
|
|
// message to the user. Using signal() in Win32 is less powerful,
|
|
// since we can only trap a subset of all possible exceptions, and
|
|
// we can't retrieve the exception address. The VC runtime implements
|
|
// signal() by surrounding main() with a __try __except block and
|
|
// calling the signal handler in the __except filter, exactly the way
|
|
// we do it here.
|
|
|
|
__try
|
|
{
|
|
#endif
|
|
|
|
// Save the C callee-save registers
|
|
// and restore the Mercury registers.
|
|
|
|
MR_save_regs_to_mem(c_regs);
|
|
MR_restore_registers();
|
|
|
|
#if defined(MR_DEBUG_THE_RUNTIME) && defined(MR_USE_GCC_NONLOCAL_GOTOS)
|
|
// Double-check to make sure that we are 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.
|
|
|
|
MR_global_pointer_2 = safety_buffer; // Defeat optimization.
|
|
MR_memset(safety_buffer, MAGIC_MARKER_2, SAFETY_BUFFER_SIZE);
|
|
#endif
|
|
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
#ifndef MR_CONSERVATIVE_GC
|
|
MR_ENGINE(MR_eng_heap_zone)->max =
|
|
MR_ENGINE(MR_eng_heap_zone)->min;
|
|
#endif
|
|
MR_CONTEXT(MR_ctxt_detstack_zone)->MR_zone_max =
|
|
MR_CONTEXT(MR_ctxt_detstack_zone)->MR_zone_min;
|
|
MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_max =
|
|
MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_min;
|
|
#endif
|
|
|
|
MR_user_time_at_start = MR_get_user_cpu_milliseconds();
|
|
MR_user_time_at_last_stat = MR_user_time_at_start;
|
|
|
|
MR_real_time_at_start = MR_get_real_milliseconds();
|
|
MR_real_time_at_last_stat = MR_real_time_at_start;
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
saved_cur_callback = MR_current_callback_site;
|
|
saved_cur_csd = MR_current_call_site_dynamic;
|
|
MR_setup_callback(MR_program_entry_point);
|
|
#endif
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
MR_threadscope_post_calling_main();
|
|
#endif
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
MR_do_interpreter();
|
|
#else
|
|
MR_debugmsg0("About to call engine\n");
|
|
(void) MR_call_engine(MR_ENTRY(MR_do_interpreter), MR_FALSE);
|
|
MR_debugmsg0("Returning from MR_call_engine()\n");
|
|
#endif
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
MR_threadscope_post_stop_context(MR_TS_STOP_REASON_FINISHED);
|
|
#endif
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
MR_current_call_site_dynamic = saved_cur_csd;
|
|
MR_current_callback_site = saved_cur_callback;
|
|
#endif
|
|
|
|
#if defined(MR_USE_GCC_NONLOCAL_GOTOS) && defined(MR_DEBUG_THE_RUNTIME)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SAFETY_BUFFER_SIZE; i++) {
|
|
MR_assert(safety_buffer[i] == MAGIC_MARKER_2);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (MR_detaildebug) {
|
|
MR_debugregs("after final call");
|
|
}
|
|
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
if (MR_memdebug) {
|
|
printf("\n");
|
|
#ifndef MR_CONSERVATIVE_GC
|
|
printf("max heap used: %6ld words\n",
|
|
(long) (MR_ENGINE(MR_eng_heap_zone)->max
|
|
- MR_ENGINE(MR_eng_heap_zone)->min));
|
|
#endif
|
|
printf("max detstack used: %6ld words\n",
|
|
(long)(MR_CONTEXT(MR_ctxt_detstack_zone)->MR_zone_max
|
|
- MR_CONTEXT(MR_ctxt_detstack_zone)->MR_zone_min));
|
|
printf("max nondetstack used: %6ld words\n",
|
|
(long) (MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_max
|
|
- MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_min));
|
|
}
|
|
#endif
|
|
|
|
#ifdef MR_MEASURE_REGISTER_USAGE
|
|
printf("\n");
|
|
MR_print_register_usage_counts();
|
|
#endif
|
|
|
|
#ifdef MR_DO_CALL_STATS
|
|
{
|
|
char *stats_file_name;
|
|
FILE *stats_fp;
|
|
|
|
stats_file_name = getenv("HO_CALL_STATS");
|
|
if (stats_file_name != NULL) {
|
|
stats_fp = fopen(stats_file_name, "a");
|
|
if (stats_fp != NULL) {
|
|
MR_print_hidden_arg_stats(stats_fp);
|
|
(void) fclose(stats_fp);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MR_TYPE_CTOR_STATS
|
|
MR_print_type_ctor_stats();
|
|
#endif
|
|
|
|
#ifdef MR_STACK_FRAME_STATS
|
|
MR_print_stack_frame_stats();
|
|
#endif // MR_STACK_FRAME_STATS
|
|
|
|
#ifdef MR_PROFILE_ZONES
|
|
MR_print_zone_stats();
|
|
#endif
|
|
|
|
// 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.
|
|
|
|
MR_save_registers();
|
|
MR_restore_regs_from_mem(c_regs);
|
|
|
|
#ifdef MR_MSVC_STRUCTURED_EXCEPTIONS
|
|
}
|
|
__except(MR_filter_win32_exception(GetExceptionInformation()))
|
|
{
|
|
// Everything is done in MR_filter_win32_exception.
|
|
}
|
|
#endif
|
|
|
|
} // end mercury_runtime_main()
|
|
|
|
#ifdef MR_TYPE_CTOR_STATS
|
|
|
|
#define MR_INIT_CTOR_NAME_ARRAY_SIZE 10
|
|
|
|
void
|
|
MR_register_type_ctor_stat(MR_TypeStat *type_stat,
|
|
MR_TypeCtorInfo type_ctor_info)
|
|
{
|
|
int i;
|
|
MR_TypeCtorRep rep;
|
|
|
|
rep = MR_type_ctor_rep(type_ctor_info);
|
|
type_stat->type_ctor_reps[MR_GET_ENUM_VALUE(rep)]++;
|
|
|
|
for (i = 0; i < type_stat->type_ctor_name_next; i++) {
|
|
// We can compare pointers instead of using strcmp,
|
|
// because the pointers in the array come from the
|
|
// type_ctor_infos themselves, and there is only one
|
|
// static type_ctor_info for each modulename.typename
|
|
// combination.
|
|
|
|
if (type_stat->type_ctor_names[i].type_stat_module ==
|
|
type_ctor_info->type_ctor_module_name &&
|
|
type_stat->type_ctor_names[i].type_stat_name ==
|
|
type_ctor_info->type_ctor_name)
|
|
{
|
|
type_stat->type_ctor_names[i].type_stat_count++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
MR_ensure_room_for_next(type_stat->type_ctor_name, MR_TypeNameStat,
|
|
MR_INIT_CTOR_NAME_ARRAY_SIZE);
|
|
|
|
i = type_stat->type_ctor_name_next;
|
|
type_stat->type_ctor_names[i].type_stat_module =
|
|
type_ctor_info->type_ctor_module_name;
|
|
type_stat->type_ctor_names[i].type_stat_name =
|
|
type_ctor_info->type_ctor_name;
|
|
type_stat->type_ctor_names[i].type_stat_ctor_rep = rep;
|
|
type_stat->type_ctor_names[i].type_stat_count = 1;
|
|
type_stat->type_ctor_name_next++;
|
|
}
|
|
|
|
static void
|
|
MR_print_type_ctor_stats(void)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fopen(MR_TYPE_CTOR_STATS, "a");
|
|
if (fp == NULL) {
|
|
return;
|
|
}
|
|
|
|
MR_print_one_type_ctor_stat(fp, "UNIFY", &MR_type_stat_mer_unify);
|
|
MR_print_one_type_ctor_stat(fp, "UNIFY_C", &MR_type_stat_c_unify);
|
|
MR_print_one_type_ctor_stat(fp, "COMPARE", &MR_type_stat_mer_compare);
|
|
MR_print_one_type_ctor_stat(fp, "COMPARE_C", &MR_type_stat_c_compare);
|
|
|
|
(void) fclose(fp);
|
|
}
|
|
|
|
static void
|
|
MR_print_one_type_ctor_stat(FILE *fp, const char *op, MR_TypeStat *type_stat)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < (int) MR_TYPECTOR_REP_UNKNOWN; i++) {
|
|
if (type_stat->type_ctor_reps[i] > 0) {
|
|
fprintf(fp, "%s %s %ld\n", op,
|
|
MR_ctor_rep_name[i], type_stat->type_ctor_reps[i]);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < type_stat->type_ctor_name_next; i++) {
|
|
fprintf(fp, "%s %s %s %s %ld\n", op,
|
|
type_stat->type_ctor_names[i].type_stat_module,
|
|
type_stat->type_ctor_names[i].type_stat_name,
|
|
MR_ctor_rep_name[MR_GET_ENUM_VALUE(type_stat->
|
|
type_ctor_names[i].type_stat_ctor_rep)],
|
|
type_stat->type_ctor_names[i].type_stat_count);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
static void
|
|
MR_do_interpreter(void)
|
|
{
|
|
#if !defined(MR_CONSERVATIVE_GC) && !defined(MR_NATIVE_GC)
|
|
// Save the heap pointer here and restore it at the end
|
|
// of this function, so that you can run benchmarks in
|
|
// a loop in grade `hlc' without running out of memory.
|
|
|
|
MR_Word *saved_hp = MR_hp;
|
|
#endif
|
|
|
|
#ifdef MR_MPROF_PROFILE_TIME
|
|
if (MR_profiling) {
|
|
MR_prof_turn_on_time_profiling();
|
|
}
|
|
#endif
|
|
|
|
// Call the entry point (normally the Mercury predicate main/2).
|
|
{
|
|
MR_Word outputs[4];
|
|
typedef void MR_CALL (*EntryPoint1)(MR_Word *);
|
|
typedef void MR_CALL (*EntryPoint2)(MR_Word *, MR_Word *);
|
|
typedef void MR_CALL (*EntryPoint3)(MR_Word *, MR_Word *, MR_Word *);
|
|
typedef void MR_CALL (*EntryPoint4)(MR_Word *, MR_Word *, MR_Word *,
|
|
MR_Word *);
|
|
switch (MR_num_output_args) {
|
|
case 0:
|
|
(*MR_program_entry_point)();
|
|
break;
|
|
|
|
case 1:
|
|
(*(EntryPoint1)MR_program_entry_point)(&outputs[0]);
|
|
break;
|
|
|
|
case 2:
|
|
(*(EntryPoint2)MR_program_entry_point)(&outputs[0],
|
|
&outputs[1]);
|
|
break;
|
|
case 3:
|
|
(*(EntryPoint3)MR_program_entry_point)(&outputs[0],
|
|
&outputs[1], &outputs[2]);
|
|
break;
|
|
|
|
case 4:
|
|
(*(EntryPoint4)MR_program_entry_point)(&outputs[0],
|
|
&outputs[1], &outputs[2], &outputs[3]);
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error("sorry, not implemented: "
|
|
"--num-output-args > 4");
|
|
}
|
|
}
|
|
|
|
#if defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE)
|
|
assert(MR_thread_equal(pthread_self(), MR_primordial_thread));
|
|
MR_LOCK(&MR_thread_barrier_lock, "MR_do_interpreter");
|
|
while (MR_thread_barrier_count > 0) {
|
|
while (MR_COND_WAIT(&MR_thread_barrier_cond, &MR_thread_barrier_lock,
|
|
"MR_do_interpreter") != 0)
|
|
;
|
|
}
|
|
MR_UNLOCK(&MR_thread_barrier_lock, "MR_do_interpreter");
|
|
#endif
|
|
|
|
#ifdef MR_MPROF_PROFILE_TIME
|
|
if (MR_profiling) {
|
|
MR_prof_turn_off_time_profiling();
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MR_CONSERVATIVE_GC) && !defined(MR_NATIVE_GC)
|
|
MR_hp_word = (MR_Word) saved_hp;
|
|
#endif
|
|
}
|
|
|
|
#else // ! MR_HIGHLEVEL_CODE
|
|
|
|
MR_define_extern_entry(MR_do_interpreter);
|
|
MR_declare_label(global_success);
|
|
MR_declare_label(global_success_2);
|
|
MR_declare_label(global_fail);
|
|
MR_declare_label(all_done);
|
|
MR_declare_label(wrapper_not_reached);
|
|
|
|
MR_BEGIN_MODULE(interpreter_module)
|
|
MR_init_entry_an(MR_do_interpreter);
|
|
MR_init_label_an(global_success);
|
|
MR_init_label_an(global_success_2);
|
|
MR_init_label_an(global_fail);
|
|
MR_init_label_an(all_done);
|
|
MR_init_label_an(wrapper_not_reached);
|
|
MR_BEGIN_CODE
|
|
|
|
MR_define_entry(MR_do_interpreter);
|
|
MR_incr_sp(4);
|
|
MR_stackvar(1) = MR_hp_word;
|
|
MR_stackvar(2) = MR_succip_word;
|
|
MR_stackvar(3) = MR_maxfr_word;
|
|
MR_stackvar(4) = MR_curfr_word;
|
|
|
|
MR_succip_word = (MR_Word) MR_LABEL(wrapper_not_reached);
|
|
MR_mkframe("interpreter", 1, MR_LABEL(global_fail));
|
|
|
|
MR_stack_trace_bottom_ip = MR_LABEL(global_success);
|
|
MR_nondet_stack_trace_bottom_fr = MR_maxfr;
|
|
#ifdef MR_STACK_SEGMENTS
|
|
// Set MR_nondet_stack_trace_bottom_zone to the zone containing MR_maxfr.
|
|
|
|
{
|
|
MR_MemoryZone *cur_zone;
|
|
MR_MemoryZones *prev_zones;
|
|
|
|
cur_zone = MR_CONTEXT(MR_ctxt_nondetstack_zone);
|
|
prev_zones = MR_CONTEXT(MR_ctxt_prev_nondetstack_zones);
|
|
while (MR_TRUE) {
|
|
if (MR_in_zone(MR_maxfr, cur_zone)) {
|
|
MR_nondet_stack_trace_bottom_zone = cur_zone;
|
|
break;
|
|
}
|
|
|
|
if (prev_zones == NULL) {
|
|
MR_fatal_error("MR_maxfr is not in a nondetstack zone");
|
|
}
|
|
|
|
cur_zone = prev_zones->MR_zones_head;
|
|
prev_zones = prev_zones->MR_zones_tail;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
if (MR_finaldebug) {
|
|
MR_save_transient_registers();
|
|
MR_printregs(stdout, "do_interpreter started");
|
|
if (MR_detaildebug) {
|
|
MR_dumpnondetstack(stdout);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (MR_program_entry_point == NULL) {
|
|
MR_fatal_error("no program entry point supplied");
|
|
}
|
|
|
|
#ifdef MR_MPROF_PROFILE_TIME
|
|
MR_set_prof_current_proc(MR_program_entry_point);
|
|
if (MR_profiling) {
|
|
MR_prof_turn_on_time_profiling();
|
|
}
|
|
#endif
|
|
|
|
MR_noprof_call(MR_program_entry_point, MR_LABEL(global_success));
|
|
|
|
MR_define_label(global_success);
|
|
// Don't let the original Mercury thread continue onto MR_global_success_2
|
|
// until all other threads have terminated.
|
|
|
|
MR_LOCK(&MR_thread_barrier_lock, "global_success");
|
|
if (MR_thread_barrier_count == 0) {
|
|
MR_UNLOCK(&MR_thread_barrier_lock, "global_success");
|
|
MR_GOTO_LABEL(global_success_2);
|
|
} else {
|
|
MR_Context *this_ctxt;
|
|
|
|
this_ctxt = MR_ENGINE(MR_eng_this_context);
|
|
MR_save_context(this_ctxt);
|
|
this_ctxt->MR_ctxt_resume = MR_LABEL(global_success_2);
|
|
MR_thread_barrier_context = this_ctxt;
|
|
MR_UNLOCK(&MR_thread_barrier_lock, "global_success");
|
|
|
|
MR_ENGINE(MR_eng_this_context) = NULL;
|
|
MR_idle();
|
|
}
|
|
|
|
MR_define_label(global_success_2);
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
if (MR_finaldebug) {
|
|
MR_save_transient_registers();
|
|
MR_printregs(stdout, "global succeeded");
|
|
if (MR_detaildebug) {
|
|
MR_dumpnondetstack(stdout);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MR_GOTO_LABEL(all_done);
|
|
|
|
MR_define_label(global_fail);
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
if (MR_finaldebug) {
|
|
MR_save_transient_registers();
|
|
MR_printregs(stdout, "global failed");
|
|
|
|
if (MR_detaildebug) {
|
|
MR_dumpnondetstack(stdout);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MR_define_label(all_done);
|
|
assert(MR_runqueue_head == NULL);
|
|
|
|
#ifdef MR_MPROF_PROFILE_TIME
|
|
if (MR_profiling) {
|
|
MR_prof_turn_off_time_profiling();
|
|
}
|
|
#endif
|
|
|
|
MR_hp_word = MR_stackvar(1);
|
|
MR_succip_word = MR_stackvar(2);
|
|
MR_maxfr_word = MR_stackvar(3);
|
|
MR_curfr_word = MR_stackvar(4);
|
|
MR_decr_sp(4);
|
|
|
|
#ifdef MR_DEBUG_THE_RUNTIME
|
|
if (MR_finaldebug && MR_detaildebug) {
|
|
MR_save_transient_registers();
|
|
MR_printregs(stdout, "after popping...");
|
|
}
|
|
#endif
|
|
|
|
MR_proceed();
|
|
#ifndef MR_USE_GCC_NONLOCAL_GOTOS
|
|
return 0;
|
|
#endif
|
|
|
|
MR_define_label(wrapper_not_reached);
|
|
MR_fatal_error("reached wrapper_not_reached");
|
|
MR_END_MODULE
|
|
|
|
#endif // MR_HIGHLEVEL_CODE
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
void
|
|
MR_dummy_main(void)
|
|
{
|
|
MR_fatal_error("invalid attempt to call through Mercury entry point.");
|
|
}
|
|
|
|
#else // ! MR_HIGHLEVEL_CODE
|
|
|
|
MR_define_extern_entry(MR_dummy_main);
|
|
|
|
MR_BEGIN_MODULE(dummy_main_module)
|
|
MR_init_entry_an(MR_dummy_main);
|
|
MR_BEGIN_CODE
|
|
|
|
MR_define_entry(MR_dummy_main);
|
|
MR_fatal_error("invalid attempt to call through Mercury entry point.");
|
|
|
|
MR_END_MODULE
|
|
|
|
#endif // ! MR_HIGHLEVEL_CODE
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
mercury_runtime_terminate(void)
|
|
{
|
|
#if MR_NUM_REAL_REGS > 0
|
|
MR_Word c_regs[MR_NUM_REAL_REGS];
|
|
#endif
|
|
// Save the callee-save registers; we are going to start using them
|
|
// as global registers variables now, which will clobber them,
|
|
// and we need to preserve them, because they are callee-save,
|
|
// and our caller may need them.
|
|
|
|
MR_save_regs_to_mem(c_regs);
|
|
|
|
// run any user-defined finalisation predicates
|
|
(*MR_address_of_final_modules_required)();
|
|
|
|
MR_trace_end();
|
|
|
|
(*MR_library_finalizer)();
|
|
|
|
// Restore the registers before calling MR_trace_final()
|
|
// as MR_trace_final() expect them to be valid.
|
|
|
|
MR_restore_registers();
|
|
|
|
MR_trace_final();
|
|
|
|
if (MR_trace_count_enabled) {
|
|
MR_trace_record_label_exec_counts(NULL);
|
|
}
|
|
|
|
#if defined(MR_MPROF_PROFILE_TIME) || defined(MR_MPROF_PROFILE_CALLS) \
|
|
|| defined(MR_MPROF_PROFILE_MEMORY)
|
|
if (MR_profiling) {
|
|
MR_prof_finish();
|
|
}
|
|
#endif
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
MR_deep_prof_turn_off_time_profiling();
|
|
if (MR_deep_profiling_save_results) {
|
|
if (MR_deep_prof_random_write == 0) {
|
|
// If MR_deep_prof_random_write is not set, always write out
|
|
// the results of deep profiling.
|
|
|
|
MR_write_out_profiling_tree();
|
|
} else {
|
|
// If MR_deep_prof_random_write is set to N, write out the results
|
|
// of deep profiling only on every Nth program run (on average).
|
|
|
|
if ((getpid() % MR_deep_prof_random_write) == 0) {
|
|
MR_write_out_profiling_tree();
|
|
}
|
|
}
|
|
}
|
|
#ifdef MR_DEEP_PROFILING_LOG
|
|
(void) fclose(MR_deep_prof_log_file);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MR_RECORD_TERM_SIZES
|
|
if (MR_complexity_save_results) {
|
|
MR_write_complexity_procs();
|
|
}
|
|
#endif
|
|
|
|
if (MR_print_table_statistics) {
|
|
MR_table_report_statistics(stdout);
|
|
}
|
|
|
|
#if !defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE)
|
|
MR_shutdown_ws_engines();
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
if (MR_ENGINE(MR_eng_ts_buffer)) {
|
|
MR_threadscope_finalize_engine(MR_thread_engine_base);
|
|
}
|
|
MR_finalize_threadscope();
|
|
#endif
|
|
|
|
assert(MR_thread_equal(MR_primordial_thread, pthread_self()));
|
|
MR_primordial_thread = MR_null_thread();
|
|
|
|
MR_finalize_context_stuff();
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_SYS_STAT_H
|
|
if (MR_mem_usage_report_prefix != NULL) {
|
|
struct stat statbuf;
|
|
char *filename;
|
|
char *cmd;
|
|
int i;
|
|
|
|
for (i = 1; i < MAX_MEM_USAGE_REPORT_ATTEMPTS; i++) {
|
|
filename = MR_make_string(MR_ALLOC_SITE_RUNTIME,
|
|
"%s%02d", MR_mem_usage_report_prefix, i);
|
|
if (stat(filename, &statbuf) == 0) {
|
|
// Filename exists; try next name.
|
|
continue;
|
|
}
|
|
cmd = MR_make_string(MR_ALLOC_SITE_RUNTIME,
|
|
"cp /proc/%d/status %s", getpid(), filename);
|
|
if (system(cmd) != 0) {
|
|
fprintf(stderr, "%s: cannot write memory usage report\n",
|
|
MR_progname);
|
|
// There is no point in aborting.
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif // MR_HAVE_SYS_STAT_H
|
|
|
|
MR_terminate_engine();
|
|
|
|
// Restore the callee-save registers before returning,
|
|
// since they may be used by the C code that called us.
|
|
|
|
MR_restore_regs_from_mem(c_regs);
|
|
|
|
return mercury_exit_status;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Forward decls to suppress gcc warnings.
|
|
void mercury_sys_init_wrapper_init(void);
|
|
void mercury_sys_init_wrapper_init_type_tables(void);
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_wrapper_write_out_proc_statics(FILE *fp);
|
|
#endif
|
|
|
|
void
|
|
mercury_sys_init_wrapper_init(void)
|
|
{
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
interpreter_module();
|
|
dummy_main_module();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
mercury_sys_init_wrapper_init_type_tables(void)
|
|
{
|
|
// No types to register.
|
|
}
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
void
|
|
mercury_sys_init_wrapper_write_out_proc_statics(FILE *fp)
|
|
{
|
|
// No proc_statics to write out.
|
|
}
|
|
#endif
|