// vim: ts=4 sw=4 expandtab ft=c // Copyright (C) 1994-2011 The University of Melbourne. // Copyright (C) 2014-2016, 2018 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 #include #ifdef MR_HAVE_SYS_STAT_H #include #endif #ifdef MR_MSVC_STRUCTURED_EXCEPTIONS #include #endif #ifdef MR_HAVE_FENV_H #include #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_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; static MR_bool benchmark_all_solns = MR_FALSE; static MR_bool use_own_timer = MR_FALSE; static int repeats = 1; #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; static int MR_user_time_at_finish; 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_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); void (*MR_DI_output_current_ptr)(MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_String, MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_Word, MR_Word); // normally ML_DI_output_current (output_current/13) MR_bool (*MR_DI_found_match)(MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_String, MR_Integer, MR_Integer, MR_Integer, MR_Word, MR_String, MR_Word); // normally ML_DI_found_match (output_current/12) void (*MR_DI_read_request_from_socket)(MR_Word, MR_Word *, MR_Integer *); 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_BOEHM_GC // Disable GC during startup, when little or no garbage is created. GC_disable(); #endif #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 // XXX overrides MERCURY_OPTIONS -x GC_enable(); #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)(); #else // 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 _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); } } GC_set_oom_fn(MR_oom_func); #endif // 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. progname = MR_progname; for (s = progname; *s != '\0'; s++) { if (*s == '/') { progname = s + 1; } } // 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_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_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-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 }, { "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, "acC:d:D:e:i:m:n:o:pP:r:sStT: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_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_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 'a': benchmark_all_solns = MR_TRUE; 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(); } use_own_timer = MR_FALSE; 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 'r': if (sscanf(MR_optarg, "%d", &repeats) != 1) { MR_usage(); } 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': use_own_timer = MR_TRUE; MR_calldebug = MR_FALSE; MR_nondetstackdebug = MR_FALSE; MR_detstackdebug = MR_FALSE; MR_heapdebug = MR_FALSE; MR_gotodebug = MR_FALSE; MR_sregdebug = MR_FALSE; MR_finaldebug = MR_FALSE; 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 GC_disable(); #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) { char *s; const char *exec_name; s = strrchr(MR_progname, '/'); if (s == NULL) { exec_name = MR_progname; } else { exec_name = s + 1; } 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 static int repcounter; #ifdef MR_MSVC_STRUCTURED_EXCEPTIONS // Under Win32 we use the following construction to handle exceptions. // __try // { // // } // __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; for (repcounter = 0; repcounter < repeats; repcounter++) { #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 (use_own_timer) { MR_user_time_at_finish = MR_get_user_cpu_milliseconds(); } #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 if (use_own_timer) { printf("%8.3fu ", ((double) (MR_user_time_at_finish - MR_user_time_at_start)) / 1000); } #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 if (benchmark_all_solns) { MR_redo(); } else { 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