Files
mercury/bytecode/mb_disasm.c
Peter Ross d89afe3839 Merge changes from the reuse branch back onto the main branch.
Estimated hours taken: 0.25
Branches: main

Merge changes from the reuse branch back onto the main branch.

compiler/ml_unify_gen.m:
    Handle the case where the tag on the cell to be reused is unknown.

compiler/hlds_goal.m:
    Add a field which records what possible cons_ids the cell to be
    reused can be tagged with.

compiler/builtin_ops.m:
    Add the unary builtin operator strip_tag.

compiler/bytecode.m:
compiler/c_util.m:
compiler/java_util.m:
compiler/llds.m:
compiler/mlds_to_il.m:
compiler/opt_debug.m:
bytecode/mb_disasm.c:
bytecode/mb_exec.c:
    Handle the strip_tag operator.
2001-03-13 12:40:19 +00:00

1128 lines
23 KiB
C

/*
** Copyright (C) 1997,2000-2001 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
**
** Contains functions for disassembling bytecodes into human readable form
**
*/
/* Imports */
#include "mb_disasm.h"
#include <string.h>
#include "mb_module.h"
/* Exported definitions */
int MB_str_bytecode(MB_Bytecode_Addr addr, char *buffer, int buffer_len,
int indent_level);
void MB_listing(MB_Machine_State *ms, FILE *fp, MB_Bytecode_Addr start,
MB_Bytecode_Addr end, MB_Word line_len);
/* Local declarations */
/* Fills a c string buffer with to the name of a constructor id */
static void str_cons_id(MB_Cons_id cons_id, char *buffer, int buffer_len);
/* Fills a c string buffer with to the name of a tag */
static void str_tag(MB_Tag tag, char *buffer, int buffer_len);
/* Returns a string corresponding to the name of a bytecode type */
static MB_CString_Const str_bytecode_name(MB_Byte bytecode_id);
/* Returns a string corresponding to the name of a test type */
static MB_CString_Const str_test_id(MB_Test_id test_id);
/* Returns a string corresponding to the name of a determinism type */
static MB_CString_Const str_determinism_name(MB_Byte determinism_id);
/* Returns a string corresponding to the name of a unary operation */
static MB_CString_Const str_unop_name(MB_Byte unop);
/* Returns a string corresponding to the name of a binary operation */
static MB_CString_Const str_binop_name(MB_Byte binop);
/* Fills a buffer with a string transformed into a source-style c string
* (includes double quotes around string) */
static void quote_cstring(MB_CString str, char *buffer, int buffer_len);
/* Fills a c string buffer with a variable list */
static void str_var_dir(MB_Var_dir var_dir, char *buffer, int buffer_len);
/* Fills a c string buffer with an operation argument */
static void str_op_arg(MB_Op_arg op_arg, char *buffer, int buffer_len);
/* Implementation */
/*
** Macros for printing:
** Expects buffer & buffer_len to be defined.
** Wraps calls to snprintf, checks if the buffer is full and
** if it is, returns from the function
*/
#define PRINT() if (buffer_len > 1) { \
int last_len; \
assert(buffer_len > 0); \
last_len = snprintf(buffer, buffer_len,
/* printf arguments get sandwiched between these macros */
#define ENDPRINT() ); \
if (last_len >= buffer_len) { \
last_len = buffer_len-1;\
} \
buffer += last_len; \
buffer_len -= last_len; \
assert(buffer_len > 0); \
} else { \
assert(buffer_len >= 0); \
if (buffer_len == 0) { \
buffer--; \
buffer_len++; \
} \
}
/*
** Call this after calling a function that has added characters to the buffer
** Requires that if the function filled the buffer, it must have put a null
** terminator at the end
*/
#define PRINTFILLCHECK() { \
int str_len = strlen(buffer); \
buffer += str_len; \
buffer_len -= str_len; \
assert(buffer_len > 0); \
}
/*
** Macro to call a function of the format f(arg, buffer, buffer_len)
** where the function fills all or part of the buffer
*/
#define PRINTCALL(x, y) (x)((y), buffer, buffer_len); \
PRINTFILLCHECK()
/*
** Fills a string corresponding to a bytecode
** Returns indent level for subsequent instructions
**
** If buffer is NULL or buffer_len is <= 0 then only returns
** new indent level
**
** The general convention is for arguments to be in square brackets and
** calculated arguments (ie not in the bytecode file) in round brackets
*/
int
MB_str_bytecode(MB_Bytecode_Addr addr, char *buffer,
int buffer_len, int indent_level)
{
MB_Byte bc_id = MB_code_get_id(addr);
MB_Bytecode_Arg *bca = MB_code_get_arg(addr);
/* calculate indent changes */
int this_indent = indent_level;
int next_indent = indent_level;
switch (bc_id) {
case MB_BC_enter_pred:
case MB_BC_enter_proc:
case MB_BC_enter_disjunction:
case MB_BC_enter_disjunct:
case MB_BC_enter_switch:
case MB_BC_enter_switch_arm:
case MB_BC_enter_if:
case MB_BC_enter_negation:
case MB_BC_enter_commit:
next_indent++;
break;
case MB_BC_label:
case MB_BC_enter_then:
case MB_BC_endof_then:
case MB_BC_enter_else:
case MB_BC_endof_negation_goal:
this_indent--;
break;
case MB_BC_endof_pred:
case MB_BC_endof_proc:
case MB_BC_endof_disjunction:
case MB_BC_endof_disjunct:
case MB_BC_endof_switch:
case MB_BC_endof_switch_arm:
case MB_BC_endof_if:
case MB_BC_endof_negation:
case MB_BC_endof_commit:
this_indent--;
next_indent--;
break;
case MB_BC_assign:
case MB_BC_test:
case MB_BC_construct:
case MB_BC_deconstruct:
case MB_BC_complex_construct:
case MB_BC_complex_deconstruct:
case MB_BC_place_arg:
case MB_BC_pickup_arg:
case MB_BC_call:
case MB_BC_higher_order_call:
case MB_BC_builtin_binop:
case MB_BC_builtin_unop:
case MB_BC_builtin_bintest:
case MB_BC_builtin_untest:
case MB_BC_semidet_succeed:
case MB_BC_semidet_success_check:
case MB_BC_fail:
case MB_BC_context:
case MB_BC_not_supported:
break;
default:
assert(FALSE);
}
if (next_indent < 0) next_indent = 0;
/* if we only wanted to calculate the indents, return now */
if (buffer == NULL || buffer_len <= 0) return next_indent;
/* print the indents */
while (this_indent > 0) {
PRINT()
" "
ENDPRINT()
this_indent--;
}
PRINT()
"%s",
str_bytecode_name(bc_id)
ENDPRINT()
switch (bc_id) {
case MB_BC_enter_pred:
PRINT()
" %s %s/%d (%d procs)",
bca->enter_pred.is_func ? "func" : "pred",
bca->enter_pred.pred_name,
(int) bca->enter_pred.pred_arity,
(int) bca->enter_pred.proc_count
ENDPRINT()
break;
case MB_BC_endof_pred:
/* No args */
buffer[0] = 0;
break;
case MB_BC_enter_proc: {
MB_Short i;
MB_Short len;
PRINT()
" mode %d: [%s] [%d labels] [endlabel %p]"
" [%d temps] [%d vars]",
(int) bca->enter_proc.mode_num,
str_determinism_name(bca->enter_proc.det),
bca->enter_proc.label_count,
bca->enter_proc.end_label.addr,
bca->enter_proc.temp_count,
bca->enter_proc.list_length
ENDPRINT()
len = bca->enter_proc.list_length;
for (i = 0; i < len; i++) {
PRINT()
" %s",
bca->enter_proc.var_info[i]
ENDPRINT()
}
break;
}
case MB_BC_endof_proc:
PRINT()
" (%p)",
bca->endof_proc.proc_start
ENDPRINT()
break;
case MB_BC_label:
PRINT()
" %d",
(int) bca->label.label
ENDPRINT()
break;
case MB_BC_enter_disjunction:
PRINT()
" [endlabel %p]",
bca->enter_disjunction.end_label.addr
ENDPRINT()
break;
case MB_BC_endof_disjunction:
/* No args */
buffer[0] = 0;
break;
case MB_BC_enter_disjunct:
PRINT()
" [nextlabel %p]",
bca->enter_disjunct.next_label.addr
ENDPRINT()
break;
case MB_BC_endof_disjunct:
PRINT()
" [endlabel %p]",
bca->endof_disjunct.end_label.addr
ENDPRINT()
break;
case MB_BC_enter_switch:
PRINT()
" [var %d] [endlabel %p]",
(int) bca->enter_switch.var,
bca->enter_switch.end_label.addr
ENDPRINT()
break;
case MB_BC_endof_switch:
/* No args */
buffer[0] = 0;
break;
case MB_BC_enter_switch_arm:
PRINT()
" (on var %d) ",
(int) bca->enter_switch_arm.var
ENDPRINT()
PRINTCALL(str_cons_id, bca->enter_switch_arm.cons_id)
PRINT()
" [nextlabel %p]",
bca->enter_switch_arm.next_label.addr
ENDPRINT()
break;
case MB_BC_endof_switch_arm:
PRINT()
" [endlabel %p]",
bca->endof_switch_arm.end_label.addr
ENDPRINT()
break;
case MB_BC_enter_if:
PRINT()
" [else %p] [end %p] [frame %d]",
bca->enter_if.else_label.addr,
bca->enter_if.end_label.addr,
(int) bca->enter_if.frame_ptr_tmp
ENDPRINT()
break;
case MB_BC_enter_then:
PRINT()
" [frame %d]",
(int) bca->enter_then.frame_ptr_tmp
ENDPRINT()
break;
case MB_BC_endof_then:
PRINT()
" [follow %p]",
bca->endof_then.follow_label.addr
ENDPRINT()
break;
case MB_BC_enter_else:
PRINT()
" [frame %d]",
(int) bca->enter_else.frame_ptr_tmp
ENDPRINT()
break;
case MB_BC_endof_if:
/* No args */
buffer[0] = 0;
break;
case MB_BC_enter_negation:
PRINT()
" [frame %d] [endlabel %p]",
(int) bca->enter_negation.frame_ptr_tmp,
bca->enter_negation.end_label.addr
ENDPRINT()
break;
case MB_BC_endof_negation_goal:
PRINT()
" [frame %d]",
(int) bca->endof_negation_goal.frame_ptr_tmp
ENDPRINT()
case MB_BC_endof_negation:
/* No args */
buffer[0] = 0;
break;
case MB_BC_enter_commit:
PRINT()
" [frame %d]",
(int) bca->enter_commit.frame_ptr_tmp
ENDPRINT()
break;
case MB_BC_endof_commit:
PRINT()
" [frame %d]",
(int) bca->endof_commit.frame_ptr_tmp
ENDPRINT()
break;
case MB_BC_assign:
PRINT()
" [var %d] <= [var %d]",
(int) bca->assign.to_var,
(int) bca->assign.from_var
ENDPRINT()
break;
case MB_BC_test:
PRINT()
" [var %d] == [var %d] [type %s]",
(int) bca->test.var1,
(int) bca->test.var2,
str_test_id(bca->test.id)
ENDPRINT()
break;
case MB_BC_construct: {
MB_Short len;
MB_Short i;
PRINT()
" [var %d] <= ",
(int) bca->construct.to_var
ENDPRINT()
PRINTCALL(str_cons_id,bca->construct.consid)
len = bca->construct.list_length;
PRINT()
" [%d var%s%s",
(int) len,
(len != 0) ? "s" : "",
(len > 0) ? ":" : ""
ENDPRINT()
for (i = 0; i < len; i++) {
PRINT()
" %d",
(int) bca->construct.var_list[i]
ENDPRINT()
}
PRINT()
"]"
ENDPRINT()
break;
}
case MB_BC_deconstruct: {
MB_Short len;
MB_Short i;
PRINT()
" [var %d] ",
(int) bca->deconstruct.from_var
ENDPRINT()
PRINTCALL(str_cons_id,bca->deconstruct.consid)
len = bca->deconstruct.list_length;
PRINT()
" [%d var%s%s",
(int) len,
(len != 0) ? "s" : "",
(len > 0) ? ":" : ""
ENDPRINT()
for (i = 0; i < len; i++) {
PRINT()
" %d",
(int) bca->deconstruct.var_list[i]
ENDPRINT()
}
PRINT()
"]"
ENDPRINT()
break;
}
case MB_BC_complex_construct: {
MB_Short len;
MB_Short i;
PRINT()
" %d ",
(int) bca->complex_construct.to_var
ENDPRINT()
PRINTCALL(str_cons_id,bca->complex_construct.consid);
len = bca->complex_construct.list_length;
PRINT()
" [%d var%s]",
(int) len,
(len == 1) ? "" : "s"
ENDPRINT()
for (i = 0; i < len; i++) {
PRINT()
" "
ENDPRINT()
PRINTCALL(str_var_dir,
bca->complex_construct.var_dir[i])
}
break;
}
case MB_BC_complex_deconstruct: {
MB_Short len;
MB_Short i;
PRINT()
" %d ",
(int) bca->complex_deconstruct.from_var
ENDPRINT()
PRINTCALL(str_cons_id,bca->complex_deconstruct.consid)
len = bca->complex_deconstruct.list_length;
PRINT()
" [%d var%s]",
(int) len,
(len == 1) ? "" : "s"
ENDPRINT()
for (i = 0; i < len; i++) {
PRINT()
" "
ENDPRINT()
PRINTCALL(str_var_dir,
bca->complex_deconstruct.var_dir[i])
}
break;
}
case MB_BC_place_arg:
PRINT()
" [r%d] <= [var %d]",
(int) bca->place_arg.to_reg,
(int) bca->place_arg.from_var
ENDPRINT()
break;
case MB_BC_pickup_arg:
PRINT()
" [r%d] => [var %d]",
(int) bca->pickup_arg.from_reg,
(int) bca->pickup_arg.to_var
ENDPRINT()
break;
case MB_BC_call:
PRINT()
" [%s %s__%s/%d mode %d (%s %p)]",
bca->call.is_func ? "func" : "pred",
bca->call.module_name,
bca->call.pred_name,
(int) bca->call.arity,
(int) bca->call.mode_num,
bca->call.addr.is_native ? "natv" : "byte",
bca->call.addr.is_native ?
(MB_Word *) bca->call.addr.addr.native :
(MB_Word *) bca->call.addr.addr.bc
ENDPRINT()
break;
case MB_BC_higher_order_call:
PRINT()
" [var %d] [invars %d] [outvars %d] [%s]",
(int) bca->higher_order_call.pred_var,
(int) bca->higher_order_call.in_var_count,
(int) bca->higher_order_call.out_var_count,
str_determinism_name(bca->higher_order_call.det)
ENDPRINT()
break;
case MB_BC_builtin_binop:
PRINT()
": "
ENDPRINT()
PRINTCALL(str_op_arg,bca->builtin_binop.arg1)
PRINT()
" %s ",
str_binop_name(bca->builtin_binop.binop)
ENDPRINT()
PRINTCALL(str_op_arg,bca->builtin_binop.arg2)
PRINT()
" => [var %d]",
(int) bca->builtin_binop.to_var
ENDPRINT()
break;
case MB_BC_builtin_unop:
PRINT()
" %s ",
str_unop_name(bca->builtin_unop.unop)
ENDPRINT()
PRINTCALL(str_op_arg, bca->builtin_unop.arg)
PRINT()
" => [var %d]",
(int) bca->builtin_unop.to_var
ENDPRINT()
break;
case MB_BC_builtin_bintest:
PRINT()
" "
ENDPRINT()
PRINTCALL(str_op_arg,bca->builtin_binop.arg1)
PRINT()
" %s ",
str_binop_name(bca->builtin_bintest.binop)
ENDPRINT()
PRINTCALL(str_op_arg,bca->builtin_binop.arg2)
break;
case MB_BC_builtin_untest:
PRINT()
" %s ",
str_unop_name(bca->builtin_untest.unop)
ENDPRINT()
PRINTCALL(str_op_arg,bca->builtin_unop.arg)
break;
case MB_BC_semidet_succeed:
/* No args */
buffer[0] = 0;
break;
case MB_BC_semidet_success_check:
/* No args */
buffer[0] = 0;
break;
case MB_BC_fail:
/* No args */
buffer[0] = 0;
break;
case MB_BC_context:
PRINT()
" %d",
bca->context.line_number
ENDPRINT()
break;
case MB_BC_not_supported:
/* No args */
buffer[0] = 0;
break;
default:
MB_fatal("Attempt to disassemble unknown bytecode");
break;
} /* end switch */
return next_indent;
} /* end str_bytecode() */
static void
str_cons_id(MB_Cons_id cons_id, char *buffer, int buffer_len)
{
PRINT()
"["
ENDPRINT()
switch (cons_id.id) {
case MB_CONSID_CONS: {
PRINT()
"functor %s__%s/%d ",
cons_id.opt.cons.module_name,
cons_id.opt.cons.string,
(int) cons_id.opt.cons.arity
ENDPRINT()
PRINTCALL(str_tag, cons_id.opt.cons.tag)
break;
}
case MB_CONSID_INT_CONST:
PRINT()
"int_const " MB_FMT_INT " (" MB_FMT_HEX ")",
cons_id.opt.int_const,
cons_id.opt.int_const
ENDPRINT()
break;
case MB_CONSID_STRING_CONST: {
PRINT()
"string_const"
ENDPRINT()
PRINTCALL(quote_cstring, cons_id.opt.string_const);
break;
}
case MB_CONSID_FLOAT_CONST:
PRINT()
"float_const %.15g",
cons_id.opt.float_const
ENDPRINT()
break;
case MB_CONSID_PRED_CONST:
PRINT()
"%s %s %s__%s/%d mode %d (%s %p)",
"pred_const",
cons_id.opt.pred_const.is_func ?"func":"pred",
cons_id.opt.pred_const.module_name,
cons_id.opt.pred_const.pred_name,
(int) cons_id.opt.pred_const.arity,
(int) cons_id.opt.pred_const.mode_num,
"natv",
(MB_Word *) cons_id.opt.pred_const.native_addr
ENDPRINT()
break;
case MB_CONSID_CODE_ADDR_CONST:
PRINT()
"%s %s %s %d %d",
"code_addr_const",
cons_id.opt.code_addr_const.module_name,
cons_id.opt.code_addr_const.pred_name,
(int) cons_id.opt.code_addr_const.arity,
(int) cons_id.opt.code_addr_const.mode_num
ENDPRINT()
break;
case MB_CONSID_BASE_TYPE_INFO_CONST:
PRINT()
"%s %s__%s/%d",
"base_type_info_const",
cons_id.opt.base_type_info_const.module_name,
cons_id.opt.base_type_info_const.type_name,
(int) cons_id.opt.base_type_info_const
.type_arity
ENDPRINT()
break;
case MB_CONSID_CHAR_CONST:
if (isprint(cons_id.opt.char_const.ch)) {
PRINT()
"%s '%c'",
"char_const",
cons_id.opt.char_const.ch
ENDPRINT()
} else {
PRINT()
"%s %d (0x%02X)",
"char_const",
(int) cons_id.opt.char_const.ch,
(int) cons_id.opt.char_const.ch
ENDPRINT()
}
break;
default:
MB_fatal("Attempt to disassemble unknown cons");
break;
} /* end switch */
PRINT()
"]"
ENDPRINT()
buffer[buffer_len-1] = 0; /* snprintf may not do it if a long string */
} /* end str_cons_id() */
static void
str_tag(MB_Tag tag, char *buffer, int buffer_len)
{
switch (tag.id) {
case MB_TAG_SIMPLE:
snprintf(buffer, buffer_len,
"%s %d",
"simple_tag",
(int) tag.opt.primary);
break;
case MB_TAG_COMPLICATED:
snprintf(buffer, buffer_len,
"%s %d %ld",
"complicated_tag",
(int) tag.opt.pair.primary,
(long int) tag.opt.pair.secondary);
break;
case MB_TAG_COMPLICATED_CONSTANT:
snprintf(buffer, buffer_len,
"%s %d %ld",
"complicated_constant_tag",
(int) tag.opt.pair.primary,
(long int) tag.opt.pair.secondary);
break;
case MB_TAG_ENUM:
snprintf(buffer, buffer_len,
"%s %d",
"enum_tag",
(int) tag.opt.enum_tag);
break;
case MB_TAG_NONE:
snprintf(buffer, buffer_len,
"%s",
"no_tag");
break;
default:
MB_util_error("Invalid tag: %d\n", tag.id);
assert(FALSE); /* XXX */
break;
} /* end switch */
/* snprintf may not append a null character */
buffer[buffer_len-1] = 0;
} /* end str_tag() */
/*
** XXX ORDER: Currently we depend on the order of elements in the table.
*/
static const char *
bytecode_table[] = {
"enter_pred",
"endof_pred",
"enter_proc",
"endof_proc",
"label",
"enter_disjunction",
"endof_disjunction",
"enter_disjunct",
"endof_disjunct",
"enter_switch",
"endof_switch",
"enter_switch_arm",
"endof_switch_arm",
"enter_if",
"enter_then",
"endof_then",
"endof_if",
"enter_negation",
"endof_negation",
"enter_commit",
"endof_commit",
"assign",
"test",
"construct",
"deconstruct",
"complex_construct",
"complex_deconstruct",
"place_arg ",
"pickup_arg",
"call",
"higher_order_call",
"builtin_binop",
"builtin_unop",
"builtin_bintest",
"builtin_untest",
"semidet_succeed",
"semidet_success_check",
"fail",
"context",
"not_supported",
"enter_else",
"endof_negation_goal"
};
static MB_CString_Const
str_bytecode_name(MB_Byte bytecode_id)
{
if (bytecode_id >= sizeof(bytecode_table) / sizeof(*bytecode_table)) {
return (MB_CString_Const) "<<unknown bytecode>>"; /* XXX */
} else {
return (MB_CString_Const) bytecode_table[bytecode_id];
}
}
/*
** XXX ORDER: Currently we depend on the order of elements in the table.
*/
static const char *
test_id_table[] = {
"int",
"char",
"string",
"float",
"user"
};
/* Returns a string corresponding to the name of a test type */
static MB_CString_Const
str_test_id(MB_Test_id test_id)
{
if (test_id >= sizeof(test_id_table) / sizeof(*test_id_table)) {
return "<<unknown test type";
} else {
return (MB_CString_Const) test_id_table[test_id];
}
}
/*
** XXX ORDER: Currently we depend on the order of elements in the table.
*/
static const char *
determinism_table[] = {
"det",
"semidet",
"multidet",
"nondet",
"cc_multidet",
"cc_nondet",
"erroneous",
"failure"
};
/* Returns a string corresponding to the name of a determinism type */
static MB_CString_Const
str_determinism_name(MB_Byte determinism_id)
{
if (determinism_id >=
sizeof(determinism_table) / sizeof(*determinism_table))
{
return (MB_CString_Const) "<<unknown determinism>>"; /* XXX */
} else {
return (MB_CString_Const) determinism_table[determinism_id];
}
}
/*
** XXX ORDER: Currently we depend on the order of elements in the table.
*/
static const char *
unop_table[] = {
"mktag",
"tag",
"unmktag",
"mkbody",
"unmkbody",
"strip_tag",
"hash_string",
"bitwise_complement",
"not"
};
/* Return a string corresponding to the name of a unary operation */
static MB_CString_Const
str_unop_name(MB_Byte unop)
{
/* bounds check */
if (unop >= sizeof(unop_table) / sizeof(*unop_table)) {
return (MB_CString_Const) "<<unknown unop>>"; /* XXX */
} else {
return (MB_CString_Const) unop_table[unop];
}
}
/*
** XXX ORDER: Currently we depend on the order of elements in the table.
*/
static const char *
binop_table[] = {
"+",
"-",
"*",
"/",
"mod",
"<<",
">>",
"&",
"|",
"^",
"and",
"or",
"eq",
"ne",
"array_index",
"str_eq",
"str_ne",
"str_lt",
"str_gt",
"str_le",
"str_ge",
"<",
">",
"<=",
">=",
"float_plus",
"float_minus",
"float_times",
"float_divide",
"float_eq",
"float_ne",
"float_lt",
"float_gt",
"float_le",
"float_ge",
"body"
}; /* end binop_table */
static MB_CString_Const
str_binop_name(MB_Byte binop)
{
/* bounds check */
if (binop >= sizeof(binop_table) / sizeof(*binop_table)) {
return (MB_CString_Const) "<<unknown binop>>"; /* XXX */
} else {
return (MB_CString_Const) binop_table[binop];
}
} /* end str_binop_name() */
/* Fills a buffer with a string transformed into a source-style c string
* (includes double quotes around string) */
static void
quote_cstring(MB_CString str, char * buffer, int buffer_len)
{
int i; /* index into str */
int j; /* index into buffer */
i = j = 0;
if (j < buffer_len) {
buffer[j] = '"';
j++;
}
while (buffer_len-1 > j) {
/*
** According to K&R 2nd ed, page 194, string literals
** may not contain newline or double-quote; they must be
** escaped. (And of course the backslash must also be
** escaped.) There is no mention of other limitations
** on what characters may be within a string literal.
*/
switch(str[i]) {
/* following two cases are identical */
case '\\':
case '\"':
buffer[j] = '\\';
buffer[j+1] = str[i];
j += 2;
i++;
break;
case '\n':
buffer[j] = '\\';
buffer[j+1] = 'n';
j += 2;
i++;
break;
case '\0': {
buffer[j] = '"';
buffer[j+1] = 0;
return;
}
default:
buffer[j] = str[i];
j++;
i++;
break;
} /* end switch */
} /* end for */
buffer[buffer_len-1] = 0;
} /* end quote_cstring() */
static void
str_var_dir(MB_Var_dir var_dir, char *buffer, int buffer_len)
{
PRINT()
"<<var_dir>>"
ENDPRINT()
return;
} /* end str_var_dir() */
static void
str_op_arg(MB_Op_arg op_arg, char *buffer, int buffer_len)
{
switch (op_arg.id) {
case MB_ARG_VAR:
PRINT()
"[var %d]",
op_arg.opt.var
ENDPRINT()
break;
case MB_ARG_INT_CONST:
PRINT()
"[int " MB_FMT_INT " (" MB_FMT_HEX ")]",
op_arg.opt.int_const,
op_arg.opt.int_const
ENDPRINT()
break;
case MB_ARG_FLOAT_CONST:
PRINT()
"[float %f]",
op_arg.opt.float_const
ENDPRINT()
break;
default:
assert(FALSE); /* XXX */
break;
} /* end switch */
} /* end str_op_arg() */
/*
** Displays a code listing from address start to end
** ms may be NULL if desired
*/
void
MB_listing(MB_Machine_State *ms, FILE *fp, MB_Bytecode_Addr start,
MB_Bytecode_Addr end, MB_Word line_len)
{
char buffer[256];
MB_Word indent = 0;
MB_Bytecode_Addr i;
MB_Bytecode_Addr ip = (ms != NULL) ? MB_ip_get(ms) : NULL;
start = MB_code_range_clamp(start);
end = MB_code_range_clamp(end);
if (sizeof(buffer) < line_len) line_len = sizeof(buffer);
/*
** backtrack to the previous predicate
** and assume that it is at indent level 0
*/
i = MB_code_get_pred_addr(start);
if (i != MB_CODE_INVALID_ADR) {
/* work out the indent level at the start */
while (i != start) {
indent = MB_str_bytecode(i, NULL, 0, indent);
i++;
}
}
/* Show the code */
for (; i != end+1; i++) {
indent = MB_str_bytecode(i, buffer, line_len, indent);
fprintf(fp, "%s%p %s\n",
(i == ip) ? "-> " : " ",
i,
buffer);
}
}