Files
mercury/util/mkinit.c
Zoltan Somogyi 09141817ba Fergus's recent change to the handling of some builtins broke the tracing
Estimated hours taken: 20

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	s/multidet/multi/

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

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

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

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

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

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

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

493 lines
11 KiB
C

/*---------------------------------------------------------------------------*/
/*
** Copyright (C) 1995-1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU General
** Public License - see the file COPYING in the Mercury distribution.
*/
/*
** File: mkinit.c
** Main authors: zs, fjh
**
** Given a list of .c or .init files on the command line, this program
** produces the initialization file (usually called *_init.c) on stdout.
** The initialization file is a small C program that calls the initialization
** functions for all the modules in a Mercury program.
*/
/*---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "mercury_getopt.h"
#include "mercury_conf.h"
#include "mercury_std.h"
/* --- adjustable limits --- */
#define MAXCALLS 40 /* maximum number of calls per function */
#define MAXLINE 256 /* maximum number of characters per line */
/* (characters after this limit are ignored) */
/* --- global variables --- */
static const char *progname = NULL;
/* options and arguments, set by parse_options() */
static const char *entry_point = "mercury__io__main_2_0";
static int maxcalls = MAXCALLS;
static int num_files;
static char **files;
static bool output_main_func = TRUE;
static bool c_files_contain_extra_inits = FALSE;
static int num_modules = 0;
static int num_errors = 0;
/* --- code fragments to put in the output file --- */
static const char header1[] =
"/*\n"
"** This code automatically generated by mkinit - do not edit.\n"
"**\n"
"** Input files:\n"
"**\n"
;
static const char header2[] =
"*/\n"
"\n"
"#include <stddef.h>\n"
"#include \"mercury_init.h\"\n"
"\n"
"/*\n"
"** Work around a bug in the Solaris 2.X (X<=4) linker;\n"
"** on these machines, init_gc must be statically linked.\n"
"*/\n"
"\n"
"#ifdef CONSERVATIVE_GC\n"
"static void init_gc(void)\n"
"{\n"
" GC_INIT();\n"
"}\n"
"#endif\n"
"\n"
;
static const char mercury_funcs[] =
"\n"
"Declare_entry(%s);\n"
"Declare_entry(mercury__io__print_3_0);\n"
"\n"
"#ifdef CONSERVATIVE_GC\n"
"extern char *GC_stackbottom;\n"
"#endif\n"
"\n"
"#if defined(USE_DLLS)\n"
" #if !defined(libmer_DEFINE_DLL)\n"
" #define libmer_impure_ptr \\\n"
" (*__imp_libmer_impure_ptr)\n"
" extern void *libmer_impure_ptr;\n"
" #endif\n"
" #if !defined(libmercury_DEFINE_DLL)\n"
" #define libmercury_impure_ptr \\\n"
" (*__imp_libmercury_impure_ptr)\n"
" extern void *libmercury_impure_ptr;\n"
" #endif\n"
"#endif\n"
"\n"
"void\n"
"mercury_init(int argc, char **argv, char *stackbottom)\n"
"{\n"
"\n"
"#ifdef CONSERVATIVE_GC\n"
" /*\n"
" ** Explicitly register the bottom of the stack, so that the\n"
" ** GC knows where it starts. This is necessary for AIX 4.1\n"
" ** on RS/6000, and for gnu-win32 on Windows 95 or NT.\n"
" ** it may also be helpful on other systems.\n"
" */\n"
" GC_stackbottom = stackbottom;\n"
"#endif\n"
"\n"
"/*\n"
"** If we're using DLLs on gnu-win32, then we need\n"
"** to take special steps to initialize _impure_ptr\n"
"** for the DLLs.\n"
"*/\n"
"#if defined(USE_DLLS)\n"
" #if !defined(libmer_DEFINE_DLL)\n"
" libmer_impure_ptr = _impure_ptr;\n"
" #endif\n"
" #if !defined(libmercury_DEFINE_DLL)\n"
" libmercury_impure_ptr = _impure_ptr;\n"
" #endif\n"
"#endif\n"
"\n"
" address_of_mercury_init_io = mercury_init_io;\n"
" address_of_init_modules = init_modules;\n"
"#ifdef CONSERVATIVE_GC\n"
" address_of_init_gc = init_gc;\n"
"#endif\n"
" MR_library_initializer = ML_io_init_state;\n"
" MR_library_finalizer = ML_io_finalize_state;\n"
" MR_library_trace_browser = ENTRY(mercury__io__print_3_0);\n"
"#ifdef MR_USE_EXTERNAL_DEBUGGER\n"
" MR_DI_output_current = ML_DI_output_current;\n"
" MR_DI_found_match = ML_DI_found_match;\n"
" MR_DI_read_request_from_socket = ML_DI_read_request_from_socket;\n"
"#endif\n"
"#if defined(USE_GCC_NONLOCAL_GOTOS) && !defined(USE_ASM_LABELS)\n"
" do_init_modules();\n"
"#endif\n"
" program_entry_point = ENTRY(%s);\n"
"\n"
" return mercury_runtime_init(argc, argv);\n"
"}\n"
"\n"
"void\n"
"mercury_call_main(void)\n"
"{\n"
" mercury_runtime_main();\n"
"}\n"
"\n"
"int\n"
"mercury_terminate(void)\n"
"{\n"
" return mercury_runtime_terminate();\n"
"}\n"
"\n"
"int\n"
"mercury_main(int argc, char **argv)\n"
"{\n"
" char dummy;\n"
" mercury_init(argc, argv, &dummy);\n"
" mercury_call_main();\n"
" return mercury_terminate();\n"
"}\n"
;
static const char main_func[] =
"\n"
"int\n"
"main(int argc, char **argv)\n"
"{\n"
" return mercury_main(argc, argv);\n"
"}\n"
;
/* --- function prototypes --- */
static void parse_options(int argc, char *argv[]);
static void usage(void);
static void output_headers(void);
static void output_sub_init_functions(void);
static void output_main_init_function(void);
static void output_main(void);
static void process_file(char *filename);
static void process_c_file(char *filename);
static void process_init_file(const char *filename);
static void output_init_function(const char *func_name);
static int getline(FILE *file, char *line, int line_max);
/*---------------------------------------------------------------------------*/
#ifndef HAVE_STRERROR
/*
** Apparently SunOS 4.1.3 doesn't have strerror()
** (!%^&!^% non-ANSI systems, grumble...)
**
** This code is duplicated in runtime/mercury_prof.c.
*/
extern int sys_nerr;
extern char *sys_errlist[];
char *
strerror(int errnum)
{
if (errnum >= 0 && errnum < sys_nerr && sys_errlist[errnum] != NULL) {
return sys_errlist[errnum];
} else {
static char buf[30];
sprintf(buf, "Error %d", errnum);
return buf;
}
}
#endif
/*---------------------------------------------------------------------------*/
int
main(int argc, char **argv)
{
progname = argv[0];
parse_options(argc, argv);
output_headers();
output_sub_init_functions();
output_main_init_function();
output_main();
if (num_errors > 0) {
fputs("/* Force syntax error, since there were */\n", stdout);
fputs("/* errors in the generation of this file */\n", stdout);
fputs("#error \"You need to remake this file\"\n", stdout);
exit(1);
}
exit(0);
}
/*---------------------------------------------------------------------------*/
static void
parse_options(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "c:w:lx")) != EOF) {
switch (c) {
case 'c':
if (sscanf(optarg, "%d", &maxcalls) != 1)
usage();
break;
case 'w':
entry_point = optarg;
break;
case 'l':
output_main_func = FALSE;
break;
case 'x':
c_files_contain_extra_inits = TRUE;
break;
default:
usage();
}
}
num_files = argc - optind;
if (num_files <= 0)
usage();
files = argv + optind;
}
static void
usage(void)
{
fprintf(stderr,
"Usage: mkinit [-c maxcalls] [-w entry] [-l] [-x] files...\n");
exit(1);
}
/*---------------------------------------------------------------------------*/
static void
output_headers(void)
{
int filenum;
fputs(header1, stdout);
for (filenum = 0; filenum < num_files; filenum++) {
fputs("** ", stdout);
fputs(files[filenum], stdout);
putc('\n', stdout);
}
fputs(header2, stdout);
}
static void
output_sub_init_functions(void)
{
int filenum;
fputs("static void init_modules_0(void)\n", stdout);
fputs("{\n", stdout);
for (filenum = 0; filenum < num_files; filenum++) {
process_file(files[filenum]);
}
fputs("}\n\n", stdout);
}
static void
output_main_init_function(void)
{
int i;
fputs("static void init_modules(void)\n", stdout);
fputs("{\n", stdout);
for (i = 0; i <= num_modules; i++) {
printf("\tinit_modules_%d();\n", i);
}
fputs("}\n", stdout);
}
static void
output_main(void)
{
printf(mercury_funcs, entry_point, entry_point);
if (output_main_func) {
fputs(main_func, stdout);
}
}
/*---------------------------------------------------------------------------*/
static void
process_file(char *filename)
{
int len = strlen(filename);
/*
** XXX the following three lines are needed only for bootstrapping;
** they should be deleted once the new compiler has been installed
** everywhere.
*/
if (len >= 2 && strcmp(filename + len - 2, ".m") == 0) {
process_c_file(filename);
} else
if (len >= 2 && strcmp(filename + len - 2, ".c") == 0) {
if (c_files_contain_extra_inits) {
process_init_file(filename);
} else {
process_c_file(filename);
}
} else if (len >= 5 && strcmp(filename + len - 5, ".init") == 0) {
process_init_file(filename);
} else {
fprintf(stderr,
"%s: filename `%s' must end in `.c' or `.init'\n",
progname, filename);
num_errors++;
}
}
static void
process_c_file(char *filename)
{
char func_name[1000];
char *position;
/* remove the trailing ".c" */
filename[strlen(filename) - 2] = '\0';
/* remove the directory name, if any */
if ((position = strrchr(filename, '/')) != NULL) {
filename = position + 1;
}
/*
** The func name is "mercury__<modulename>__init",
** where <modulename> is the base filename with all
** `.'s replaced with `__'.
*/
strcpy(func_name, "mercury");
while ((position = strchr(filename, '.')) != NULL) {
strcat(func_name, "__");
strncat(func_name, filename, position - filename);
filename = position + 1;
}
strcat(func_name, "__");
strcat(func_name, filename);
strcat(func_name, "__init");
output_init_function(func_name);
}
static void
process_init_file(const char *filename)
{
const char * const init_str = "INIT ";
const char * const endinit_str = "ENDINIT ";
const int init_strlen = strlen(init_str);
const int endinit_strlen = strlen(endinit_str);
char line[MAXLINE];
FILE * cfile;
cfile = fopen(filename, "r");
if (cfile == NULL) {
fprintf(stderr, "%s: error opening file `%s': %s\n",
progname, filename, strerror(errno));
num_errors++;
return;
}
while (getline(cfile, line, MAXLINE) > 0) {
if (strncmp(line, init_str, init_strlen) == 0) {
int j;
for (j = init_strlen; isalnum(line[j]) ||
line[j] == '_'; j++)
{
/* VOID */
}
line[j] = '\0';
output_init_function(line+init_strlen);
}
if (strncmp(line, endinit_str, endinit_strlen) == 0) {
break;
}
}
fclose(cfile);
}
static void
output_init_function(const char *func_name)
{
static int num_calls = 0;
if (num_calls >= maxcalls) {
printf("}\n\n");
num_modules++;
num_calls = 0;
printf("static void init_modules_%d(void)\n", num_modules);
printf("{\n");
}
num_calls++;
printf("\t{ extern void %s(void);\n", func_name);
printf("\t %s(); }\n", func_name);
}
/*---------------------------------------------------------------------------*/
static int
getline(FILE *file, char *line, int line_max)
{
int c, num_chars, limit;
num_chars = 0;
limit = line_max - 2;
while ((c = getc(file)) != EOF && c != '\n') {
if (num_chars < limit) {
line[num_chars++] = c;
}
}
if (c == '\n' || num_chars > 0) {
line[num_chars++] = '\n';
}
line[num_chars] = '\0';
return num_chars;
}
/*---------------------------------------------------------------------------*/