diff options
author | Jan Brittenson <bson@gnu.org> | 1993-09-21 14:25:24 -0700 |
---|---|---|
committer | Jan Brittenson <bson@gnu.org> | 1993-09-21 14:25:24 -0700 |
commit | ca695ac93dca6da6f9bcb2916cd3798f016084b2 (patch) | |
tree | 9955b06ba9da7010dd96c146915e52676d09d15f | |
parent | 86d7f2db057abae09db4208bf0578f6e9a0da17b (diff) | |
download | gcc-ca695ac93dca6da6f9bcb2916cd3798f016084b2.zip gcc-ca695ac93dca6da6f9bcb2916cd3798f016084b2.tar.gz gcc-ca695ac93dca6da6f9bcb2916cd3798f016084b2.tar.bz2 |
bytecode
From-SVN: r5379
-rw-r--r-- | gcc/Makefile.in | 83 | ||||
-rw-r--r-- | gcc/c-pragma.c | 36 | ||||
-rw-r--r-- | gcc/emit-rtl.c | 41 | ||||
-rw-r--r-- | gcc/expr.c | 1308 | ||||
-rw-r--r-- | gcc/integrate.c | 10 | ||||
-rw-r--r-- | gcc/regclass.c | 8 | ||||
-rw-r--r-- | gcc/rtl.h | 17 | ||||
-rw-r--r-- | gcc/stmt.c | 930 | ||||
-rw-r--r-- | gcc/toplev.c | 153 | ||||
-rw-r--r-- | gcc/varasm.c | 554 |
10 files changed, 2939 insertions, 201 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index e9993c9..2c044ee 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -399,6 +399,9 @@ CPLUS_OBJS = cp-parse.o cp-decl.o cp-decl2.o \ cp-expr.o cp-pt.o cp-edsel.o cp-xref.o \ $(CPLUS_INPUT) cp-spew.o c-common.o +# Files specific to the C interpreter bytecode compiler(s). +BC_OBJS = bc-emit.o bc-optab.o + # Language-independent object files. OBJS = toplev.o version.o tree.o print-tree.o stor-layout.o fold-const.o \ function.o stmt.o expr.o calls.o expmed.o explow.o optabs.o varasm.o \ @@ -461,6 +464,7 @@ CONFIG_H = RTL_H = rtl.h rtl.def machmode.h machmode.def TREE_H = tree.h real.h tree.def machmode.h machmode.def CPLUS_TREE_H = $(TREE_H) cp-tree.h cp-tree.def +BYTECODE_H = bytecode.h bc-emit.h bc-optab.h # Avoid a lot of time thinking about remaking Makefile.in and *.def. .SUFFIXES: .in .def @@ -484,7 +488,7 @@ for-bootstrap: start.encap $(LIBGCC) rest.encap: $(LIBGCC) stmp-headers $(STMP_FIXPROTO) $(EXTRA_PARTS) # This is what is made with the host's compiler # whether making a cross compiler or not. -native: config.status cpp $(LANGUAGES) $(EXTRA_PASSES) $(EXTRA_PROGRAMS) $(USE_COLLECT2) +native: bytecode config.status cpp $(LANGUAGES) $(EXTRA_PASSES) $(EXTRA_PROGRAMS) $(USE_COLLECT2) # Define the names for selecting languages in LANGUAGES. C c: cc1 @@ -545,14 +549,14 @@ g++-cross: $(srcdir)/g++.c $(CC) $(ALL_CFLAGS) $(INCLUDES) $(LDFLAGS) -o g++-cross \ -DGCC_NAME=\"$(target)-gcc\" $(srcdir)/g++.c version.o $(LIBS) -cc1:$(P) $(C_OBJS) $(OBJS) $(LIBDEPS) - $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1 $(C_OBJS) $(OBJS) $(LIBS) +cc1:$(P) $(C_OBJS) $(OBJS) $(BC_OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1 $(C_OBJS) $(OBJS) $(BC_OBJS) $(LIBS) -cc1plus:$(P) $(CPLUS_OBJS) $(OBJS) $(LIBDEPS) - $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1plus $(CPLUS_OBJS) $(OBJS) $(LIBS) +cc1plus:$(P) $(CPLUS_OBJS) $(OBJS) $(BC_OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1plus $(CPLUS_OBJS) $(BC_OBJS) $(OBJS) $(LIBS) -cc1obj:$(P) $(OBJC_OBJS) $(OBJS) $(LIBDEPS) - $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1obj $(OBJC_OBJS) $(OBJS) $(LIBS) +cc1obj:$(P) $(OBJC_OBJS) $(OBJS) $(BC_OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1obj $(OBJC_OBJS) $(OBJS) $(BC_OBJS) $(LIBS) # Copy float.h from its source. gfloat.h: $(FLOAT_H) @@ -1247,6 +1251,63 @@ $(HOST_PREFIX_1)malloc.o: malloc.c $(HOST_PREFIX_1): touch $(HOST_PREFIX_1) +# Remake bytecode files. +# BI_ALL=bi-run.o +BI_ALL= +BC_ALL=bc-opname.h bc-opcode.h bc-arity.h +BI_OBJ=bi-parser.o bi-lexer.o bi-reverse.o + + +bc-emit.o : bc-emit.c $(CONFIG_H) $(BYTECODE_H) +bc-optab.o : bc-optab.c bc-typecd.def $(CONFIG_H) $(BYTECODE_H) + + +bytecode: $(BI_ALL) $(BC_ALL) + +bi-arity: bi-arity.o +bi-opcode: bi-opcode.o +bi-opname: bi-opname.o +bi-unparse: bi-unparse.o +bi-lexer: bi-lexer.o + +bi-arity bi-opcode bi-opname bi-unparse bi-lexer: $(BI_OBJ) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ $^ $(LEXLIB) + +bi-run.o: $(srcdir)/bi-run.c $(srcdir)/bi-run.h $(srcdir)/bc-typecd.h bc-opname.h bc-arity.h bc-opcode.h + $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c $< + +bi-parser.c: $(srcdir)/bi-parser.y $(srcdir)/bi-parser.h + +bi-parser.o: $(srcdir)/bi-parser.c $(srcdir)/bi-defs.h + $(CC) $(CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c $< + +bi-lexer.c: $(srcdir)/bi-lexer.l $(srcdir)/bi-parser.h + +bi-lexer.o: bi-lexer.c bi-parser.h + $(CC) $(CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c $< + +bc-arity.h: $(srcdir)/bytecode.def bi-arity + -rm -f $@ + bi-arity <$< >$@ + +bc-opcode.h: $(srcdir)/bytecode.def bi-opcode + -rm -f $@ + bi-opcode <$< >$@ + +bc-opname.h: $(srcdir)/bytecode.def bi-opname + -rm -f $@ + bi-opname <$< >$@ + +bytecode.mostlyclean: + -rm -f bc-arity.h bc-opcode.h bc-opname.h + +bytecode.distclean bytecode.clean: bytecode.mostlyclean + -rm -f bi-arity bi-opcode bi-opname bi-unparse bi-lexer + +bytecode.realclean: bytecode.clean + -rm -f bi-parser.c bi-lexer.c bi-parser.h + + # Remake cpp and protoize. # Making the preprocessor @@ -1507,7 +1568,7 @@ $(srcdir)/INSTALL: install1.texi install.texi # `realclean' also deletes everything that could be regenerated automatically. -mostlyclean: +mostlyclean: bytecode.mostlyclean -rm -f $(STAGESTUFF) # Clean the objc subdir if we created one. if [ -d objc ]; then \ @@ -1545,7 +1606,7 @@ mostlyclean: # Delete all files made by compilation # that don't exist in the distribution. -clean: mostlyclean +clean: mostlyclean bytecode.clean # It may not be quite desirable to delete unprotoize.c here, # but the spec for `make clean' requires it. # Using unprotoize.c is not quite right in the first place, @@ -1557,7 +1618,7 @@ clean: mostlyclean # Delete all files that users would normally create # while building and installing GCC. -distclean: clean +distclean: clean bytecode.distclean -rm -f tm.h aux-output.c config.h md config.status tconfig.h hconfig.h -rm -f Makefile *.oaux -rm -fr stage1 stage2 stage3 stage4 @@ -1581,7 +1642,7 @@ extraclean: distclean # Get rid of every file that's generated from some other file. # Most of these files ARE PRESENT in the GCC distribution. -realclean: distclean +realclean: distclean bytecode.realclean -rm -f c-parse.y objc-parse.y -rm -f cp-parse.c cp-parse.h cp-parse.output -rm -f objc-parse.c objc-parse.output diff --git a/gcc/c-pragma.c b/gcc/c-pragma.c index 9bb9266..002c094 100644 --- a/gcc/c-pragma.c +++ b/gcc/c-pragma.c @@ -20,6 +20,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdio.h> #include "config.h" #include "tree.h" +#include "function.h" #ifdef HANDLE_SYSV_PRAGMA @@ -45,20 +46,7 @@ handle_pragma_token (string, token) char *string; tree token; { - static enum pragma_state - { - ps_start, - ps_done, - ps_bad, - ps_weak, - ps_name, - ps_equals, - ps_value, - ps_pack, - ps_left, - ps_align, - ps_right - } state = ps_start, type; + static enum pragma_state state = ps_start, type; static char *name; static char *value; static int align; @@ -76,24 +64,8 @@ handle_pragma_token (string, token) { #ifdef HANDLE_PRAGMA_WEAK if (HANDLE_PRAGMA_WEAK) - { - if (state == ps_name || state == ps_value) - { - fprintf (asm_out_file, "\t%s\t", WEAK_ASM_OP); - ASM_OUTPUT_LABELREF (asm_out_file, name); - fputc ('\n', asm_out_file); - if (state == ps_value) - { - fprintf (asm_out_file, "\t%s\t", SET_ASM_OP); - ASM_OUTPUT_LABELREF (asm_out_file, name); - fputc (',', asm_out_file); - ASM_OUTPUT_LABELREF (asm_out_file, value); - fputc ('\n', asm_out_file); - } - } - else if (! (state == ps_done || state == ps_start)) - warning ("malformed `#pragma weak'"); - } + handle_pragma_weak (state, asm_out_file, name, value); + #endif /* HANDLE_PRAMA_WEAK */ } diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 04b9069..a927fd0 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -42,8 +42,29 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "regs.h" #include "insn-config.h" #include "real.h" +#include "obstack.h" + +#include "bytecode.h" +#include "machmode.h" +#include "bc-opcode.h" +#include "bc-typecd.h" +#include "bc-optab.h" +#include "bc-emit.h" + #include <stdio.h> + +/* Opcode names */ +#ifdef BCDEBUG_PRINT_CODE +char *opcode_name[] = +{ +#include "bc-opname.h" + +"***END***" +}; +#endif + + /* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function. After rtl generation, it is 1 plus the largest register number used. */ @@ -203,6 +224,11 @@ extern int emit_lineno; rtx change_address (); void init_emit (); +extern struct obstack *rtl_obstack; + +extern int stack_depth; +extern int max_stack_depth; + /* rtx gen_rtx (code, mode, [element1, ..., elementn]) ** ** This routine generates an RTX of the size specified by @@ -1216,8 +1242,12 @@ change_address (memref, mode, addr) rtx gen_label_rtx () { - register rtx label = gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, - label_num++, NULL_PTR); + register rtx label; + + label = output_bytecode + ? bc_gen_rtx (0, 0, bc_get_bytecode_label ()) + : gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, label_num++, NULL_PTR); + LABEL_NUSES (label) = 0; return label; } @@ -2559,6 +2589,13 @@ emit_line_note (file, line) char *file; int line; { + if (output_bytecode) + { + /* FIXME: for now we do nothing, but eventually we will have to deal with + debugging information. */ + return 0; + } + emit_filename = file; emit_lineno = line; @@ -19,8 +19,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" +#include "machmode.h" #include "rtl.h" #include "tree.h" +#include "obstack.h" #include "flags.h" #include "function.h" #include "insn-flags.h" @@ -31,6 +33,13 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "output.h" #include "typeclass.h" +#include "bytecode.h" +#include "bc-opcode.h" +#include "bc-typecd.h" +#include "bc-optab.h" +#include "bc-emit.h" + + #define CEIL(x,y) (((x) + (y) - 1) / (y)) /* Decide whether a function's arguments should be processed @@ -137,6 +146,20 @@ static rtx expand_builtin_apply_args PROTO((void)); static rtx expand_builtin_apply PROTO((rtx, rtx, rtx)); static void expand_builtin_return PROTO((rtx)); static rtx expand_increment PROTO((tree, int)); +rtx bc_expand_increment PROTO((struct increment_operator *, tree)); +tree bc_runtime_type_code PROTO((tree)); +rtx bc_allocate_local PROTO((int, int)); +void bc_store_memory PROTO((tree, tree)); +tree bc_expand_component_address PROTO((tree)); +tree bc_expand_address PROTO((tree)); +void bc_expand_constructor PROTO((tree)); +void bc_adjust_stack PROTO((int)); +tree bc_canonicalize_array_ref PROTO((tree)); +void bc_load_memory PROTO((tree, tree)); +void bc_load_externaddr PROTO((rtx)); +void bc_load_externaddr_id PROTO((tree, int)); +void bc_load_localaddr PROTO((rtx)); +void bc_load_parmaddr PROTO((rtx)); static void preexpand_calls PROTO((tree)); static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx)); static void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx)); @@ -183,6 +206,32 @@ enum insn_code movstr_optab[NUM_MACHINE_MODES]; #define OUTGOING_REGNO(IN) (IN) #endif +/* Maps used to convert modes to const, load, and store bytecodes. */ +enum bytecode_opcode mode_to_const_map[MAX_MACHINE_MODE]; +enum bytecode_opcode mode_to_load_map[MAX_MACHINE_MODE]; +enum bytecode_opcode mode_to_store_map[MAX_MACHINE_MODE]; + +/* Initialize maps used to convert modes to const, load, and store + bytecodes. */ +void +bc_init_mode_to_opcode_maps () +{ + int mode; + + for (mode = 0; mode < MAX_MACHINE_MODE; mode++) + mode_to_const_map[mode] = + mode_to_load_map[mode] = + mode_to_store_map[mode] = neverneverland; + +#define DEF_MODEMAP(SYM, CODE, UCODE, CONST, LOAD, STORE) \ + mode_to_const_map[(enum machine_mode) SYM] = CONST; \ + mode_to_load_map[(enum machine_mode) SYM] = LOAD; \ + mode_to_store_map[(enum machine_mode) SYM] = STORE; + +#include "modemap.def" +#undef DEF_MODEMAP +} + /* This is run once per compilation to set up which modes can be used directly in memory and to initialize the block move optab. */ @@ -2224,6 +2273,22 @@ expand_assignment (to, from, want_value, suggest_reg) return want_value ? result : NULL_RTX; } + if (output_bytecode) + { + tree dest_innermost; + + bc_expand_expr (from); + bc_emit_instruction (dup); + + dest_innermost = bc_expand_address (to); + + /* Can't deduce from TYPE that we're dealing with a bitfield, so + take care of it here. */ + + bc_store_memory (TREE_TYPE (to), dest_innermost); + return NULL; + } + /* Assignment of a structure component needs special treatment if the structure component's rtx is not simply a MEM. Assignment of an array element at a constant index @@ -3428,6 +3493,7 @@ expand_expr (exp, target, tmode, modifier) /* Use subtarget as the target for operand 0 of a binary operation. */ rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); rtx original_target = target; + /* Maybe defer this until sure not doing bytecode? */ int ignore = (target == const0_rtx || ((code == NON_LVALUE_EXPR || code == NOP_EXPR || code == CONVERT_EXPR || code == REFERENCE_EXPR @@ -3435,6 +3501,13 @@ expand_expr (exp, target, tmode, modifier) && TREE_CODE (type) == VOID_TYPE)); tree context; + + if (output_bytecode) + { + bc_expand_expr (exp); + return NULL; + } + /* Don't use hard regs as subtargets, because the combiner can only handle pseudo regs. */ if (subtarget && REGNO (subtarget) < FIRST_PSEUDO_REGISTER) @@ -5615,6 +5688,510 @@ expand_expr (exp, target, tmode, modifier) abort (); return temp; } + + +/* Emit bytecode to evaluate the given expression EXP to the stack. */ +void +bc_expand_expr (exp) + tree exp; +{ + enum tree_code code; + tree type, arg0; + rtx r; + struct binary_operator *binoptab; + struct unary_operator *unoptab; + struct increment_operator *incroptab; + struct bc_label *lab, *lab1; + enum bytecode_opcode opcode; + + + code = TREE_CODE (exp); + + switch (code) + { + case PARM_DECL: + + if (DECL_RTL (exp) == 0) + { + error_with_decl (exp, "prior parameter's size depends on `%s'"); + return; + } + + bc_load_parmaddr (DECL_RTL (exp)); + bc_load_memory (TREE_TYPE (exp), exp); + + return; + + case VAR_DECL: + + if (DECL_RTL (exp) == 0) + abort (); + +#if 0 + if (DECL_RTL (exp)->label) + bc_load_externaddr (DECL_RTL (exp)); + else + bc_load_localaddr (DECL_RTL (exp)); +#endif + if (TREE_PUBLIC (exp)) + bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp), DECL_RTL (exp)->offset); + else + bc_load_localaddr (DECL_RTL (exp)); + + bc_load_memory (TREE_TYPE (exp), exp); + return; + + case INTEGER_CST: + +#ifdef DEBUG_PRINT_CODE + fprintf (stderr, " [%x]\n", TREE_INT_CST_LOW (exp)); +#endif + bc_emit_instruction (mode_to_const_map[DECL_BIT_FIELD (exp) + ? SImode + : TYPE_MODE (TREE_TYPE (exp))], + (HOST_WIDE_INT) TREE_INT_CST_LOW (exp)); + return; + + case REAL_CST: + +#ifdef DEBUG_PRINT_CODE + fprintf (stderr, " [%g]\n", (double) TREE_INT_CST_LOW (exp)); +#endif + bc_emit_instruction (mode_to_const_map[TYPE_MODE (TREE_TYPE (exp))], + (double) TREE_REAL_CST (exp)); + return; + + case CALL_EXPR: + + /* We build a call description vector describing the type of + the return value and of the arguments; this call vector, + together with a pointer to a location for the return value + and the base of the argument list, is passed to the low + level machine dependent call subroutine, which is responsible + for putting the arguments wherever real functions expect + them, as well as getting the return value back. */ + { + tree calldesc = 0, arg; + int nargs = 0, i; + rtx retval; + + /* Push the evaluated args on the evaluation stack in reverse + order. Also make an entry for each arg in the calldesc + vector while we're at it. */ + + TREE_OPERAND (exp, 1) = nreverse (TREE_OPERAND (exp, 1)); + + for (arg = TREE_OPERAND (exp, 1); arg; arg = TREE_CHAIN (arg)) + { + ++nargs; + bc_expand_expr (TREE_VALUE (arg)); + + calldesc = tree_cons ((tree) 0, + size_in_bytes (TREE_TYPE (TREE_VALUE (arg))), + calldesc); + calldesc = tree_cons ((tree) 0, + bc_runtime_type_code (TREE_TYPE (TREE_VALUE (arg))), + calldesc); + } + + TREE_OPERAND (exp, 1) = nreverse (TREE_OPERAND (exp, 1)); + + /* Allocate a location for the return value and push its + address on the evaluation stack. Also make an entry + at the front of the calldesc for the return value type. */ + + type = TREE_TYPE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0)))); + retval = bc_allocate_local (int_size_in_bytes (type), TYPE_ALIGN (type)); + bc_load_localaddr (retval); + + calldesc = tree_cons ((tree) 0, size_in_bytes (type), calldesc); + calldesc = tree_cons ((tree) 0, bc_runtime_type_code (type), calldesc); + + /* Prepend the argument count. */ + calldesc = tree_cons ((tree) 0, + build_int_2 (nargs, 0), + calldesc); + + /* Push the address of the call description vector on the stack. */ + calldesc = build_nt (CONSTRUCTOR, (tree) 0, calldesc); + TREE_TYPE (calldesc) = build_array_type (integer_type_node, + build_index_type (build_int_2 (nargs * 2, 0))); + r = output_constant_def (calldesc); + bc_load_externaddr (r); + + /* Push the address of the function to be called. */ + bc_expand_expr (TREE_OPERAND (exp, 0)); + + /* Call the function, popping its address and the calldesc vector + address off the evaluation stack in the process. */ + bc_emit_instruction (call); + + /* Pop the arguments off the stack. */ + bc_adjust_stack (nargs); + + /* Load the return value onto the stack. */ + bc_load_localaddr (retval); + bc_load_memory (type, TREE_OPERAND (exp, 0)); + } + return; + + case SAVE_EXPR: + + if (!SAVE_EXPR_RTL (exp)) + { + /* First time around: copy to local variable */ + SAVE_EXPR_RTL (exp) = bc_allocate_local (int_size_in_bytes (TREE_TYPE (exp)), + TYPE_ALIGN (TREE_TYPE(exp))); + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_emit_instruction (dup); + + bc_load_localaddr (SAVE_EXPR_RTL (exp)); + bc_store_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0)); + } + else + { + /* Consecutive reference: use saved copy */ + bc_load_localaddr (SAVE_EXPR_RTL (exp)); + bc_load_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0)); + } + return; + +#if 0 + /* FIXME: the XXXX_STMT codes have been removed in GCC2, but + how are they handled instead? */ + case LET_STMT: + + TREE_USED (exp) = 1; + bc_expand_expr (STMT_BODY (exp)); + return; +#endif + + case NOP_EXPR: + case CONVERT_EXPR: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_expand_conversion (TREE_TYPE (TREE_OPERAND (exp, 0)), TREE_TYPE (exp)); + return; + + case MODIFY_EXPR: + + expand_assignment (TREE_TYPE (exp), TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1)); + return; + + case ADDR_EXPR: + + bc_expand_address (TREE_OPERAND (exp, 0)); + return; + + case INDIRECT_REF: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_load_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0)); + return; + + case ARRAY_REF: + + bc_expand_expr (bc_canonicalize_array_ref (exp)); + return; + + case COMPONENT_REF: + + bc_expand_component_address (exp); + + /* If we have a bitfield, generate a proper load */ + bc_load_memory (TREE_TYPE (TREE_OPERAND (exp, 1)), TREE_OPERAND (exp, 1)); + return; + + case COMPOUND_EXPR: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_emit_instruction (drop); + bc_expand_expr (TREE_OPERAND (exp, 1)); + return; + + case COND_EXPR: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 0))); + lab = bc_get_bytecode_label (); + bc_emit_bytecode (jumpifnot); + bc_emit_bytecode_labelref (lab); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + bc_expand_expr (TREE_OPERAND (exp, 1)); + lab1 = bc_get_bytecode_label (); + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (lab1); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + bc_emit_bytecode_labeldef (lab); + bc_expand_expr (TREE_OPERAND (exp, 2)); + bc_emit_bytecode_labeldef (lab1); + return; + + case TRUTH_ANDIF_EXPR: + + opcode = jumpifnot; + goto andorif; + + case TRUTH_ORIF_EXPR: + + opcode = jumpif; + goto andorif; + + case PLUS_EXPR: + + binoptab = optab_plus_expr; + goto binop; + + case MINUS_EXPR: + + binoptab = optab_minus_expr; + goto binop; + + case MULT_EXPR: + + binoptab = optab_mult_expr; + goto binop; + + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + case CEIL_DIV_EXPR: + case ROUND_DIV_EXPR: + case EXACT_DIV_EXPR: + + binoptab = optab_trunc_div_expr; + goto binop; + + case TRUNC_MOD_EXPR: + case FLOOR_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + + binoptab = optab_trunc_mod_expr; + goto binop; + + case FIX_ROUND_EXPR: + case FIX_FLOOR_EXPR: + case FIX_CEIL_EXPR: + abort (); /* Not used for C. */ + + case FIX_TRUNC_EXPR: + case FLOAT_EXPR: + case MAX_EXPR: + case MIN_EXPR: + case FFS_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + abort (); /* FIXME */ + + case RDIV_EXPR: + + binoptab = optab_rdiv_expr; + goto binop; + + case BIT_AND_EXPR: + + binoptab = optab_bit_and_expr; + goto binop; + + case BIT_IOR_EXPR: + + binoptab = optab_bit_ior_expr; + goto binop; + + case BIT_XOR_EXPR: + + binoptab = optab_bit_xor_expr; + goto binop; + + case LSHIFT_EXPR: + + binoptab = optab_lshift_expr; + goto binop; + + case RSHIFT_EXPR: + + binoptab = optab_rshift_expr; + goto binop; + + case TRUTH_AND_EXPR: + + binoptab = optab_truth_and_expr; + goto binop; + + case TRUTH_OR_EXPR: + + binoptab = optab_truth_or_expr; + goto binop; + + case LT_EXPR: + + binoptab = optab_lt_expr; + goto binop; + + case LE_EXPR: + + binoptab = optab_le_expr; + goto binop; + + case GE_EXPR: + + binoptab = optab_ge_expr; + goto binop; + + case GT_EXPR: + + binoptab = optab_gt_expr; + goto binop; + + case EQ_EXPR: + + binoptab = optab_eq_expr; + goto binop; + + case NE_EXPR: + + binoptab = optab_ne_expr; + goto binop; + + case NEGATE_EXPR: + + unoptab = optab_negate_expr; + goto unop; + + case BIT_NOT_EXPR: + + unoptab = optab_bit_not_expr; + goto unop; + + case TRUTH_NOT_EXPR: + + unoptab = optab_truth_not_expr; + goto unop; + + case PREDECREMENT_EXPR: + + incroptab = optab_predecrement_expr; + goto increment; + + case PREINCREMENT_EXPR: + + incroptab = optab_preincrement_expr; + goto increment; + + case POSTDECREMENT_EXPR: + + incroptab = optab_postdecrement_expr; + goto increment; + + case POSTINCREMENT_EXPR: + + incroptab = optab_postincrement_expr; + goto increment; + + case CONSTRUCTOR: + + bc_expand_constructor (exp); + return; + + case ERROR_MARK: + case RTL_EXPR: + + return; + + case BIND_EXPR: + { + tree vars = TREE_OPERAND (exp, 0); + int vars_need_expansion = 0; + + /* Need to open a binding contour here because + if there are any cleanups they most be contained here. */ + expand_start_bindings (0); + + /* Mark the corresponding BLOCK for output. */ + if (TREE_OPERAND (exp, 2) != 0) + TREE_USED (TREE_OPERAND (exp, 2)) = 1; + + /* If VARS have not yet been expanded, expand them now. */ + while (vars) + { + if (DECL_RTL (vars) == 0) + { + vars_need_expansion = 1; + bc_expand_decl (vars, 0); + } + bc_expand_decl_init (vars); + vars = TREE_CHAIN (vars); + } + + bc_expand_expr (TREE_OPERAND (exp, 1)); + + expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0); + + return; + } + } + + abort (); + + binop: + + bc_expand_binary_operation (binoptab, TREE_TYPE (exp), + TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1)); + return; + + + unop: + + bc_expand_unary_operation (unoptab, TREE_TYPE (exp), TREE_OPERAND (exp, 0)); + return; + + + andorif: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 0))); + lab = bc_get_bytecode_label (); + + bc_emit_instruction (dup); + bc_emit_bytecode (opcode); + bc_emit_bytecode_labelref (lab); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + bc_emit_instruction (drop); + + bc_expand_expr (TREE_OPERAND (exp, 1)); + bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 1))); + bc_emit_bytecode_labeldef (lab); + return; + + + increment: + + type = TREE_TYPE (TREE_OPERAND (exp, 0)); + + /* Push the quantum. */ + bc_expand_expr (TREE_OPERAND (exp, 1)); + + /* Convert it to the lvalue's type. */ + bc_expand_conversion (TREE_TYPE (TREE_OPERAND (exp, 1)), type); + + /* Push the address of the lvalue */ + expand_expr (build1 (ADDR_EXPR, TYPE_POINTER_TO (type), TREE_OPERAND (exp, 0))); + + /* Perform actual increment */ + expand_increment (incroptab, type); + return; +} /* Return the alignment in bits of EXP, a pointer valued expression. But don't return more than MAX_ALIGN no matter what. @@ -7056,6 +7633,9 @@ expand_increment (exp, post) int op0_is_copy = 0; int single_insn = 0; + if (output_bytecode) + return bc_expand_increment (exp, post); + /* Stabilize any component ref that might need to be evaluated more than once below. */ if (!post @@ -8340,3 +8920,731 @@ do_tablejump (index, mode, range, table_label, default_label) } #endif /* HAVE_tablejump */ + + +/* Emit a suitable bytecode to load a value from memory, assuming a pointer + to that value is on the top of the stack. The resulting type is TYPE, and + the source declaration is DECL. */ + +void +bc_load_memory (type, decl) + tree type, decl; +{ + enum bytecode_opcode opcode; + + + /* Bit fields are special. We only know about signed and + unsigned ints, and enums. The latter are treated as + signed integers. */ + + if (DECL_BIT_FIELD (decl)) + if (TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == INTEGER_TYPE) + opcode = TREE_UNSIGNED (type) ? zxloadBI : sxloadBI; + else + abort (); + else + /* See corresponding comment in bc_store_memory(). */ + if (TYPE_MODE (type) == BLKmode + || TYPE_MODE (type) == VOIDmode) + return; + else + opcode = mode_to_load_map [TYPE_MODE (type)]; + + if (opcode == neverneverland) + abort (); + + bc_emit_bytecode (opcode); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + +/* Store the contents of the second stack slot to the address in the + top stack slot. DECL is the declaration of the destination and is used + to determine whether we're dealing with a bitfield. */ + +void +bc_store_memory (type, decl) + tree type, decl; +{ + enum bytecode_opcode opcode; + + + if (DECL_BIT_FIELD (decl)) + { + if (TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == INTEGER_TYPE) + opcode = sstoreBI; + else + abort (); + } + else + if (TYPE_MODE (type) == BLKmode) + { + /* Copy structure. This expands to a block copy instruction, storeBLK. + In addition to the arguments expected by the other store instructions, + it also expects a type size (SImode) on top of the stack, which is the + structure size in size units (usually bytes). The two first arguments + are already on the stack; so we just put the size on level 1. For some + other languages, the size may be variable, this is why we don't encode + it as a storeBLK literal, but rather treat it as a full-fledged expression. */ + + bc_expand_expr (TYPE_SIZE (type)); + opcode = storeBLK; + } + else + opcode = mode_to_store_map [TYPE_MODE (type)]; + + if (opcode == neverneverland) + abort (); + + bc_emit_bytecode (opcode); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + +/* Allocate local stack space sufficient to hold a value of the given + SIZE at alignment boundary ALIGNMENT bits. ALIGNMENT must be an + integral power of 2. A special case is locals of type VOID, which + have size 0 and alignment 1 - any "voidish" SIZE or ALIGNMENT is + remapped into the corresponding attribute of SI. */ + +rtx +bc_allocate_local (size, alignment) + int size, alignment; +{ + rtx retval; + int byte_alignment; + + if (size < 0) + abort (); + + /* Normalize size and alignment */ + if (!size) + size = UNITS_PER_WORD; + + if (alignment < BITS_PER_UNIT) + byte_alignment = 1 << (INT_ALIGN - 1); + else + /* Align */ + byte_alignment = alignment / BITS_PER_UNIT; + + if (local_vars_size & (byte_alignment - 1)) + local_vars_size += byte_alignment - (local_vars_size & (byte_alignment - 1)); + + retval = bc_gen_rtx ((char *) 0, local_vars_size, (struct bc_label *) 0); + local_vars_size += size; + + return retval; +} + + +/* Allocate variable-sized local array. Variable-sized arrays are + actually pointers to the address in memory where they are stored. */ + +rtx +bc_allocate_variable_array (size) + tree size; +{ + rtx retval; + const int ptralign = (1 << (PTR_ALIGN - 1)); + + /* Align pointer */ + if (local_vars_size & ptralign) + local_vars_size += ptralign - (local_vars_size & ptralign); + + /* Note down local space needed: pointer to block; also return + dummy rtx */ + + retval = bc_gen_rtx ((char *) 0, local_vars_size, (struct bc_label *) 0); + local_vars_size += POINTER_SIZE / BITS_PER_UNIT; + return retval; +} + + +/* Push the machine address for the given external variable offset. */ +void +bc_load_externaddr (externaddr) + rtx externaddr; +{ + bc_emit_bytecode (constP); + bc_emit_code_labelref (externaddr->label, externaddr->offset); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + +static char * +bc_strdup (s) + char *s; +{ + return strcpy (xmalloc ((strlen (s) + 1) * sizeof *s), s); +} + + +/* Like above, but expects an IDENTIFIER. */ +void +bc_load_externaddr_id (id, offset) + tree id; + int offset; +{ + if (!IDENTIFIER_POINTER (id)) + abort (); + + bc_emit_bytecode (constP); + bc_emit_code_labelref (bc_xstrdup (IDENTIFIER_POINTER (id)), offset); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + +/* Push the machine address for the given local variable offset. */ +void +bc_load_localaddr (localaddr) + rtx localaddr; +{ + bc_emit_instruction (localP, (HOST_WIDE_INT) localaddr->offset); +} + + +/* Push the machine address for the given parameter offset. + NOTE: offset is in bits. */ +void +bc_load_parmaddr (parmaddr) + rtx parmaddr; +{ + bc_emit_instruction (argP, (HOST_WIDE_INT) parmaddr->offset / BITS_PER_UNIT); +} + + +/* Convert a[i] into *(a + i). */ +tree +bc_canonicalize_array_ref (exp) + tree exp; +{ + tree type = TREE_TYPE (exp); + tree array_adr = build1 (ADDR_EXPR, TYPE_POINTER_TO (type), + TREE_OPERAND (exp, 0)); + tree index = TREE_OPERAND (exp, 1); + + + /* Convert the integer argument to a type the same size as a pointer + so the multiply won't overflow spuriously. */ + + if (TYPE_PRECISION (TREE_TYPE (index)) != POINTER_SIZE) + index = convert (type_for_size (POINTER_SIZE, 0), index); + + /* The array address isn't volatile even if the array is. + (Of course this isn't terribly relevant since the bytecode + translator treats nearly everything as volatile anyway.) */ + TREE_THIS_VOLATILE (array_adr) = 0; + + return build1 (INDIRECT_REF, type, + fold (build (PLUS_EXPR, + TYPE_POINTER_TO (type), + array_adr, + fold (build (MULT_EXPR, + TYPE_POINTER_TO (type), + index, + size_in_bytes (type)))))); +} + + +/* Load the address of the component referenced by the given + COMPONENT_REF expression. + + Returns innermost lvalue. */ + +tree +bc_expand_component_address (exp) + tree exp; +{ + tree tem, chain; + enum machine_mode mode; + int bitpos = 0; + HOST_WIDE_INT SIval; + + + tem = TREE_OPERAND (exp, 1); + mode = DECL_MODE (tem); + + + /* Compute cumulative bit offset for nested component refs + and array refs, and find the ultimate containing object. */ + + for (tem = exp;; tem = TREE_OPERAND (tem, 0)) + { + if (TREE_CODE (tem) == COMPONENT_REF) + bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (tem, 1))); + else + if (TREE_CODE (tem) == ARRAY_REF + && TREE_CODE (TREE_OPERAND (tem, 1)) == INTEGER_CST + && TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) == INTEGER_CST) + + bitpos += (TREE_INT_CST_LOW (TREE_OPERAND (tem, 1)) + * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tem))) + /* * TYPE_SIZE_UNIT (TREE_TYPE (tem)) */); + else + break; + } + + expand_expr (tem); + + + /* For bitfields also push their offset and size */ + if (DECL_BIT_FIELD (TREE_OPERAND (exp, 1))) + bc_push_offset_and_size (bitpos, /* DECL_SIZE_UNIT */ (TREE_OPERAND (exp, 1))); + else + if (SIval = bitpos / BITS_PER_UNIT) + bc_emit_instruction (addconstPSI, SIval); + + return (TREE_OPERAND (exp, 1)); +} + + +/* Emit code to push two SI constants */ +void +bc_push_offset_and_size (offset, size) + HOST_WIDE_INT offset, size; +{ + bc_emit_instruction (constSI, offset); + bc_emit_instruction (constSI, size); +} + + +/* Emit byte code to push the address of the given lvalue expression to + the stack. If it's a bit field, we also push offset and size info. + + Returns innermost component, which allows us to determine not only + its type, but also whether it's a bitfield. */ + +tree +bc_expand_address (exp) + tree exp; +{ + /* Safeguard */ + if (!exp || TREE_CODE (exp) == ERROR_MARK) + return (exp); + + + switch (TREE_CODE (exp)) + { + case ARRAY_REF: + + return (bc_expand_address (bc_canonicalize_array_ref (exp))); + + case COMPONENT_REF: + + return (bc_expand_component_address (exp)); + + case INDIRECT_REF: + + bc_expand_expr (TREE_OPERAND (exp, 0)); + + /* For variable-sized types: retrieve pointer. Sometimes the + TYPE_SIZE tree is NULL. Is this a bug or a feature? Let's + also make sure we have an operand, just in case... */ + + if (TREE_OPERAND (exp, 0) + && TYPE_SIZE (TREE_TYPE (TREE_OPERAND (exp, 0))) + && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_OPERAND (exp, 0)))) != INTEGER_CST) + bc_emit_instruction (loadP); + + /* If packed, also return offset and size */ + if (DECL_BIT_FIELD (TREE_OPERAND (exp, 0))) + + bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (exp, 0))), + TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (exp, 0)))); + + return (TREE_OPERAND (exp, 0)); + + case FUNCTION_DECL: + + bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp), DECL_RTL (exp)->offset); + break; + + case PARM_DECL: + + bc_load_parmaddr (DECL_RTL (exp)); + + /* For variable-sized types: retrieve pointer */ + if (TYPE_SIZE (TREE_TYPE (exp)) + && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST) + bc_emit_instruction (loadP); + + /* If packed, also return offset and size */ + if (DECL_BIT_FIELD (exp)) + bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (exp)), + TREE_INT_CST_LOW (DECL_SIZE (exp))); + + break; + + case RESULT_DECL: + + bc_emit_instruction (returnP); + break; + + case VAR_DECL: + +#if 0 + if (DECL_RTL (exp)->label) + bc_load_externaddr (DECL_RTL (exp)); +#endif + + if (DECL_EXTERNAL (exp)) + bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp), DECL_RTL (exp)->offset); + else + bc_load_localaddr (DECL_RTL (exp)); + + /* For variable-sized types: retrieve pointer */ + if (TYPE_SIZE (TREE_TYPE (exp)) + && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST) + bc_emit_instruction (loadP); + + /* If packed, also return offset and size */ + if (DECL_BIT_FIELD (exp)) + bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (exp)), + TREE_INT_CST_LOW (DECL_SIZE (exp))); + + break; + + case STRING_CST: + { + rtx r; + + bc_emit_bytecode (constP); + r = output_constant_def (exp); + bc_emit_code_labelref (r->label, r->offset); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + } + break; + + default: + + abort(); + break; + } + + /* Most lvalues don't have components. */ + return (exp); +} + + +/* Emit a type code to be used by the runtime support in handling + parameter passing. The type code consists of the machine mode + plus the minimal alignment shifted left 8 bits. */ + +tree +bc_runtime_type_code (type) + tree type; +{ + int val; + + switch (TREE_CODE (type)) + { + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case ENUMERAL_TYPE: + case POINTER_TYPE: + case RECORD_TYPE: + + val = TYPE_MODE (type) | TYPE_ALIGN (type) << 8; + break; + + case ERROR_MARK: + + val = 0; + break; + + default: + + abort (); + } + return build_int_2 (val, 0); +} + + +/* Generate constructor label */ +char * +bc_gen_constr_label () +{ + static int label_counter; + static char label[20]; + + sprintf (label, "*LR%d", label_counter++); + + return (obstack_copy0 (&permanent_obstack, label, strlen (label))); +} + + +/* Evaluate constructor CONSTR and return pointer to it on level one. We + expand the constructor data as static data, and push a pointer to it. + The pointer is put in the pointer table and is retrieved by a constP + bytecode instruction. We then loop and store each constructor member in + the corresponding component. Finally, we return the original pointer on + the stack. */ + +void +bc_expand_constructor (constr) + tree constr; +{ + char *l; + HOST_WIDE_INT ptroffs; + rtx constr_rtx; + + + /* Literal constructors are handled as constants, whereas + non-literals are evaluated and stored element by element + into the data segment. */ + + /* Allocate space in proper segment and push pointer to space on stack. + */ + + l = bc_gen_constr_label (); + + if (TREE_CONSTANT (constr)) + { + text_section (); + + bc_emit_const_labeldef (l); + bc_output_constructor (constr, int_size_in_bytes (TREE_TYPE (constr))); + } + else + { + data_section (); + + bc_emit_data_labeldef (l); + bc_output_data_constructor (constr); + } + + + /* Add reference to pointer table and recall pointer to stack; + this code is common for both types of constructors: literals + and non-literals. */ + + bc_emit_instruction (constP, (HOST_WIDE_INT) ptroffs = bc_define_pointer (l)); + + /* This is all that has to be done if it's a literal. */ + if (TREE_CONSTANT (constr)) + return; + + + /* At this point, we have the pointer to the structure on top of the stack. + Generate sequences of store_memory calls for the constructor. */ + + /* constructor type is structure */ + if (TREE_CODE (TREE_TYPE (constr)) == RECORD_TYPE) + { + register tree elt; + + /* If the constructor has fewer fields than the structure, + clear the whole structure first. */ + + if (list_length (CONSTRUCTOR_ELTS (constr)) + != list_length (TYPE_FIELDS (TREE_TYPE (constr)))) + { + bc_emit_instruction (dup); + bc_emit_instruction (constSI, (HOST_WIDE_INT) int_size_in_bytes (TREE_TYPE (constr))); + bc_emit_instruction (clearBLK); + } + + /* Store each element of the constructor into the corresponding + field of TARGET. */ + + for (elt = CONSTRUCTOR_ELTS (constr); elt; elt = TREE_CHAIN (elt)) + { + register tree field = TREE_PURPOSE (elt); + register enum machine_mode mode; + int bitsize; + int bitpos; + int unsignedp; + + bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)) /* * DECL_SIZE_UNIT (field) */; + mode = DECL_MODE (field); + unsignedp = TREE_UNSIGNED (field); + + bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + + bc_store_field (elt, bitsize, bitpos, mode, TREE_VALUE (elt), TREE_TYPE (TREE_VALUE (elt)), + /* The alignment of TARGET is + at least what its type requires. */ + VOIDmode, 0, + TYPE_ALIGN (TREE_TYPE (constr)) / BITS_PER_UNIT, + int_size_in_bytes (TREE_TYPE (constr))); + } + } + else + + /* Constructor type is array */ + if (TREE_CODE (TREE_TYPE (constr)) == ARRAY_TYPE) + { + register tree elt; + register int i; + tree domain = TYPE_DOMAIN (TREE_TYPE (constr)); + int minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)); + int maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)); + tree elttype = TREE_TYPE (TREE_TYPE (constr)); + + /* If the constructor has fewer fields than the structure, + clear the whole structure first. */ + + if (list_length (CONSTRUCTOR_ELTS (constr)) < maxelt - minelt + 1) + { + bc_emit_instruction (dup); + bc_emit_instruction (constSI, (HOST_WIDE_INT) int_size_in_bytes (TREE_TYPE (constr))); + bc_emit_instruction (clearBLK); + } + + + /* Store each element of the constructor into the corresponding + element of TARGET, determined by counting the elements. */ + + for (elt = CONSTRUCTOR_ELTS (constr), i = 0; + elt; + elt = TREE_CHAIN (elt), i++) + { + register enum machine_mode mode; + int bitsize; + int bitpos; + int unsignedp; + + mode = TYPE_MODE (elttype); + bitsize = GET_MODE_BITSIZE (mode); + unsignedp = TREE_UNSIGNED (elttype); + + bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype)) + /* * TYPE_SIZE_UNIT (elttype) */ ); + + bc_store_field (elt, bitsize, bitpos, mode, + TREE_VALUE (elt), TREE_TYPE (TREE_VALUE (elt)), + /* The alignment of TARGET is + at least what its type requires. */ + VOIDmode, 0, + TYPE_ALIGN (TREE_TYPE (constr)) / BITS_PER_UNIT, + int_size_in_bytes (TREE_TYPE (constr))); + } + + } +} + + +/* Store the value of EXP (an expression tree) into member FIELD of + structure at address on stack, which has type TYPE, mode MODE and + occupies BITSIZE bits, starting BITPOS bits from the beginning of the + structure. + + ALIGN is the alignment that TARGET is known to have, measured in bytes. + TOTAL_SIZE is its size in bytes, or -1 if variable. */ + +void +bc_store_field (field, bitsize, bitpos, mode, exp, type, + value_mode, unsignedp, align, total_size) + int bitsize, bitpos; + enum machine_mode mode; + tree field, exp, type; + enum machine_mode value_mode; + int unsignedp; + int align; + int total_size; +{ + + /* Expand expression and copy pointer */ + bc_expand_expr (exp); + bc_emit_instruction (over); + + + /* If the component is a bit field, we cannot use addressing to access + it. Use bit-field techniques to store in it. */ + + if (DECL_BIT_FIELD (field)) + { + bc_store_bit_field (bitpos, bitsize, unsignedp); + return; + } + else + /* Not bit field */ + { + HOST_WIDE_INT offset = bitpos / BITS_PER_UNIT; + + /* Advance pointer to the desired member */ + if (offset) + bc_emit_instruction (addconstPSI, offset); + + /* Store */ + bc_store_memory (type, field); + } +} + + +/* Store SI/SU in bitfield */ +void +bc_store_bit_field (offset, size, unsignedp) + int offset, size, unsignedp; +{ + /* Push bitfield offset and size */ + bc_push_offset_and_size (offset, size); + + /* Store */ + bc_emit_instruction (sstoreBI); +} + + +/* Load SI/SU from bitfield */ +void +bc_load_bit_field (offset, size, unsignedp) + int offset, size, unsignedp; +{ + /* Push bitfield offset and size */ + bc_push_offset_and_size (offset, size); + + /* Load: sign-extend if signed, else zero-extend */ + bc_emit_instruction (unsignedp ? zxloadBI : sxloadBI); +} + + +/* Adjust interpreter stack by NLEVELS. Positive means drop NLEVELS + (adjust stack pointer upwards), negative means add that number of + levels (adjust the stack pointer downwards). Only positive values + normally make sense. */ + +void +bc_adjust_stack (nlevels) + int nlevels; +{ + switch (nlevels) + { + case 0: + break; + + case 2: + bc_emit_instruction (drop); + + case 1: + bc_emit_instruction (drop); + break; + + default: + + bc_emit_instruction (adjstackSI, (HOST_WIDE_INT) nlevels); + stack_depth -= nlevels; + } + +#if defined (VALIDATE_STACK) + VALIDATE_STACK (); +#endif +} diff --git a/gcc/integrate.c b/gcc/integrate.c index a41e9b6..c5e0494 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -32,6 +32,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "integrate.h" #include "real.h" #include "function.h" +#include "bytecode.h" #include "obstack.h" #define obstack_chunk_alloc xmalloc @@ -2850,9 +2851,16 @@ void output_inline_function (fndecl) tree fndecl; { - rtx head = DECL_SAVED_INSNS (fndecl); + rtx head; rtx last; + if (output_bytecode) + { + warning ("`inline' ignored for bytecode output"); + return; + } + + head = DECL_SAVED_INSNS (fndecl); current_function_decl = fndecl; /* This call is only used to initialize global variables. */ diff --git a/gcc/regclass.c b/gcc/regclass.c index 1960355..47da72f 100644 --- a/gcc/regclass.c +++ b/gcc/regclass.c @@ -32,6 +32,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "recog.h" #include "reload.h" #include "real.h" +#include "bytecode.h" #ifndef REGISTER_MOVE_COST #define REGISTER_MOVE_COST(x, y) 2 @@ -413,6 +414,13 @@ fix_register (name, fixed, call_used) { int i; + if (output_bytecode) + { + warning ("request to mark `%s' as %s ignored by bytecode compiler", + name, call_used ? "call-used" : "fixed"); + return; + } + /* Decode the name and update the primary form of the register info. */ @@ -139,6 +139,22 @@ typedef struct rtx_def The number of operands and their types are controlled by the `code' field, according to rtl.def. */ rtunion fld[1]; + + /* The rest is used instead of the above if bytecode is being output */ + + /* For static or external objects. */ + char *label; + + /* From the named label, or the local variable pointer or the + argument pointer, depending on context. */ + + int offset; + + /* For goto labels inside bytecode functions. */ + struct bc_label *bc_label; + + /* A unique identifier */ + int uid; } *rtx; /* Add prototype support. */ @@ -640,6 +656,7 @@ extern rtx gen_rtx PROTO((enum rtx_code, enum machine_mode, ...)); extern rtvec gen_rtvec PROTO((int, ...)); #else +extern rtx bc_gen_rtx (); extern rtx gen_rtx (); extern rtvec gen_rtvec (); #endif @@ -49,6 +49,13 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "obstack.h" #include "loop.h" #include "recog.h" +#include "machmode.h" + +#include "bytecode.h" +#include "bc-typecd.h" +#include "bc-opcode.h" +#include "bc-optab.h" +#include "bc-emit.h" #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free @@ -181,13 +188,28 @@ static void emit_jump_if_reachable (); static int warn_if_unused_value (); static void expand_goto_internal (); +static void bc_expand_goto_internal (); static int expand_fixup (); +static void bc_expand_fixup (); void fixup_gotos (); +static void bc_fixup_gotos (); void free_temp_slots (); static void expand_cleanups (); static void expand_null_return_1 (); static int tail_recursion_args (); static void do_jump_if_equal (); +int bc_expand_exit_loop_if_false (); +void bc_expand_start_cond (); +void bc_expand_end_cond (); +void bc_expand_start_else (); +void bc_expand_end_bindings (); +void bc_expand_start_case (); +void bc_check_for_full_enumeration_handling (); +void bc_expand_end_case (); +void bc_expand_decl (); + +extern rtx bc_allocate_local (); +extern rtx bc_allocate_variable_array (); /* Stack of control and binding constructs we are currently inside. @@ -250,7 +272,8 @@ struct nesting /* Sequence number of this binding contour within the function, in order of entry. */ int block_start_count; - /* Nonzero => value to restore stack to on exit. */ + /* Nonzero => value to restore stack to on exit. Complemented by + bc_stack_level (see below) when generating bytecodes. */ rtx stack_level; /* The NOTE that starts this contour. Used by expand_goto to check whether the destination @@ -277,6 +300,8 @@ struct nesting struct label_chain *label_chain; /* Number of function calls seen, as of start of this block. */ int function_call_count; + /* Bytecode specific: stack level to restore stack to on exit. */ + int bc_stack_level; } block; /* For switch (C) or case (Pascal) statements, and also for dummies (see `expand_start_case_dummy'). */ @@ -285,6 +310,10 @@ struct nesting /* The insn after which the case dispatch should finally be emitted. Zero for a dummy. */ rtx start; + /* For bytecodes, the case table is in-lined right in the code. + A label is needed for skipping over this block. It is only + used when generating bytecodes. */ + rtx skip_label; /* A list of case labels, kept in ascending order by value as the list is built. During expand_end_case, this list may be rearranged into a @@ -425,6 +454,21 @@ struct goto_fixup time this goto was seen. The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */ tree cleanup_list_list; + + /* Bytecode specific members follow */ + + /* The label that this jump is jumping to, or 0 for break, continue + or return. */ + struct bc_label *bc_target; + + /* The label we use for the fixup patch */ + struct bc_label *label; + + /* True (non-0) if fixup has been handled */ + int bc_handled:1; + + /* Like stack_level above, except refers to the interpreter stack */ + int bc_stack_level; }; static struct goto_fixup *goto_fixup_chain; @@ -514,11 +558,16 @@ restore_stmt_status (p) void emit_nop () { - rtx last_insn = get_last_insn (); - if (!optimize - && (GET_CODE (last_insn) == CODE_LABEL - || prev_real_insn (last_insn) == 0)) - emit_insn (gen_nop ()); + rtx last_insn; + + if (!output_bytecode) + { + last_insn = get_last_insn (); + if (!optimize + && (GET_CODE (last_insn) == CODE_LABEL + || prev_real_insn (last_insn) == 0)) + emit_insn (gen_nop ()); + } } /* Return the rtx-label that corresponds to a LABEL_DECL, @@ -555,9 +604,17 @@ void expand_computed_goto (exp) tree exp; { - rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0); - emit_queue (); - emit_indirect_jump (x); + if (output_bytecode) + { + bc_expand_expr (exp); + bc_emit_instruction (jumpP); + } + else + { + rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0); + emit_queue (); + emit_indirect_jump (x); + } } /* Handle goto statements and the labels that they can go to. */ @@ -579,6 +636,15 @@ expand_label (label) { struct label_chain *p; + if (output_bytecode) + { + if (! DECL_RTL (label)) + DECL_RTL (label) = bc_gen_rtx ((char *) 0, 0, bc_get_bytecode_label ()); + if (! bc_emit_bytecode_labeldef (DECL_RTL (label)->bc_label)) + error ("multiply defined label"); + return; + } + do_pending_stack_adjust (); emit_label (label_rtx (label)); if (DECL_NAME (label)) @@ -620,8 +686,16 @@ void expand_goto (label) tree label; { + tree context; + + if (output_bytecode) + { + expand_goto_internal (label, label_rtx (label), NULL_RTX); + return; + } + /* Check for a nonlocal goto to a containing function. */ - tree context = decl_function_context (label); + context = decl_function_context (label); if (context != 0 && context != current_function_decl) { struct function *p = find_function_data (context); @@ -701,6 +775,16 @@ expand_goto_internal (body, label, last_insn) struct nesting *block; rtx stack_level = 0; + /* NOTICE! If a bytecode instruction other than `jump' is needed, + then the caller has to call bc_expand_goto_internal() + directly. This is rather an exceptional case, and there aren't + that many places where this is necessary. */ + if (output_bytecode) + { + expand_goto_internal (body, label, last_insn); + return; + } + if (GET_CODE (label) != CODE_LABEL) abort (); @@ -753,6 +837,77 @@ expand_goto_internal (body, label, last_insn) emit_jump (label); } +/* Generate a jump with OPCODE to the given bytecode LABEL which is + found within BODY. */ +static void +bc_expand_goto_internal (opcode, label, body) + enum bytecode_opcode opcode; + struct bc_label *label; + tree body; +{ + struct nesting *block; + int stack_level = -1; + + /* If the label is defined, adjust the stack as necessary. + If it's not defined, we have to push the reference on the + fixup list. */ + + if (label->defined) + { + + /* Find the innermost pending block that contains the label. + (Check containment by comparing bytecode uids.) Then restore the + outermost stack level within that block. */ + + for (block = block_stack; block; block = block->next) + { + if (block->data.block.first_insn->uid < label->uid) + break; + if (block->data.block.bc_stack_level) + stack_level = block->data.block.bc_stack_level; + + /* Execute the cleanups for blocks we are exiting. */ + if (block->data.block.cleanups != 0) + { + expand_cleanups (block->data.block.cleanups, NULL_TREE); + do_pending_stack_adjust (); + } + } + + /* Restore the stack level. If we need to adjust the stack, we + must do so after the jump, since the jump may depend on + what's on the stack. Thus, any stack-modifying conditional + jumps (these are the only ones that rely on what's on the + stack) go into the fixup list. */ + + if (stack_level >= 0 + && stack_depth != stack_level + && opcode != jump) + + bc_expand_fixup (opcode, label, stack_level); + else + { + if (stack_level >= 0) + bc_adjust_stack (stack_depth - stack_level); + + if (body && DECL_BIT_FIELD (body)) + error ("jump to `%s' invalidly jumps into binding contour", + IDENTIFIER_POINTER (DECL_NAME (body))); + + /* Emit immediate jump */ + bc_emit_bytecode (opcode); + bc_emit_bytecode_labelref (label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + } + } + else + /* Put goto in the fixup list */ + bc_expand_fixup (opcode, label, stack_level); +} + /* Generate if necessary a fixup for a goto whose target label in tree structure (if any) is TREE_LABEL and whose target in rtl is RTL_LABEL. @@ -884,6 +1039,37 @@ expand_fixup (tree_label, rtl_label, last_insn) return block != 0; } + +/* Generate bytecode jump with OPCODE to a fixup routine that links to LABEL. + Make the fixup restore the stack level to STACK_LEVEL. */ + +static void +bc_expand_fixup (opcode, label, stack_level) + enum bytecode_opcode opcode; + struct bc_label *label; + int stack_level; +{ + struct goto_fixup *fixup + = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); + + fixup->label = bc_get_bytecode_label (); + fixup->bc_target = label; + fixup->bc_stack_level = stack_level; + fixup->bc_handled = FALSE; + + fixup->next = goto_fixup_chain; + goto_fixup_chain = fixup; + + /* Insert a jump to the fixup code */ + bc_emit_bytecode (opcode); + bc_emit_bytecode_labelref (fixup->label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + /* When exiting a binding contour, process all pending gotos requiring fixups. THISBLOCK is the structure that describes the block being exited. STACK_LEVEL is the rtx for the stack level to restore exiting this contour. @@ -907,6 +1093,12 @@ fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) { register struct goto_fixup *f, *prev; + if (output_bytecode) + { + bc_fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in); + return; + } + /* F is the fixup we are considering; PREV is the previous one. */ /* We run this loop in two passes so that cleanups of exited blocks are run first, and blocks that are exited are marked so @@ -1039,6 +1231,72 @@ fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) f->stack_level = stack_level; } } + + +/* When exiting a binding contour, process all pending gotos requiring fixups. + Note: STACK_DEPTH is not altered. + + The arguments are currently not used in the bytecode compiler, but we may need + them one day for languages other than C. + + THISBLOCK is the structure that describes the block being exited. + STACK_LEVEL is the rtx for the stack level to restore exiting this contour. + CLEANUP_LIST is a list of expressions to evaluate on exiting this contour. + FIRST_INSN is the insn that began this contour. + + Gotos that jump out of this contour must restore the + stack level and do the cleanups before actually jumping. + + DONT_JUMP_IN nonzero means report error there is a jump into this + contour from before the beginning of the contour. + This is also done if STACK_LEVEL is nonzero. */ + +static void +bc_fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) + struct nesting *thisblock; + int stack_level; + tree cleanup_list; + rtx first_insn; + int dont_jump_in; +{ + register struct goto_fixup *f, *prev; + int saved_stack_depth; + + /* F is the fixup we are considering; PREV is the previous one. */ + + for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) + { + /* Test for a fixup that is inactive because it is already handled. */ + if (f->before_jump == 0) + { + /* Delete inactive fixup from the chain, if that is easy to do. */ + if (prev) + prev->next = f->next; + } + + /* Emit code to restore the stack and continue */ + bc_emit_bytecode_labeldef (f->label); + + /* Save stack_depth across call, since bc_adjust_stack () will alter + the perceived stack depth via the instructions generated. */ + + if (f->bc_stack_level >= 0) + { + saved_stack_depth = stack_depth; + bc_adjust_stack (stack_depth - f->bc_stack_level); + stack_depth = saved_stack_depth; + } + + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (f->bc_target); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + } + + goto_fixup_chain = NULL; +} /* Generate RTL for an asm statement (explicit assembler code). BODY is a STRING_CST node containing the assembler code text, @@ -1048,6 +1306,12 @@ void expand_asm (body) tree body; { + if (output_bytecode) + { + error ("`asm' is illegal when generating bytecode"); + return; + } + if (TREE_CODE (body) == ADDR_EXPR) body = TREE_OPERAND (body, 0); @@ -1090,6 +1354,12 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) /* The insn we have emitted. */ rtx insn; + if (output_bytecode) + { + error ("`asm' is illegal when generating bytecode"); + return; + } + /* Count the number of meaningful clobbered registers, ignoring what we would ignore later. */ nclobbers = 0; @@ -1310,6 +1580,22 @@ void expand_expr_stmt (exp) tree exp; { + if (output_bytecode) + { + int org_stack_depth = stack_depth; + + bc_expand_expr (exp); + + /* Restore stack depth */ + if (stack_depth < org_stack_depth) + abort (); + + bc_emit_instruction (drop); + + last_expr_type = TREE_TYPE (exp); + return; + } + /* If -W, warn about statements with no side effects, except for an explicit cast to void (e.g. for assert()), and except inside a ({...}) where they may be useful. */ @@ -1459,10 +1745,17 @@ clear_last_expr () tree expand_start_stmt_expr () { + int momentary; + tree t; + + /* When generating bytecode just note down the stack depth */ + if (output_bytecode) + return (build_int_2 (stack_depth, 0)); + /* Make the RTL_EXPR node temporary, not momentary, so that rtl_expr_chain doesn't become garbage. */ - int momentary = suspend_momentary (); - tree t = make_node (RTL_EXPR); + momentary = suspend_momentary (); + t = make_node (RTL_EXPR); resume_momentary (momentary); start_sequence (); NO_DEFER_POP; @@ -1486,6 +1779,38 @@ tree expand_end_stmt_expr (t) tree t; { + if (output_bytecode) + { + int i; + tree t; + + + /* At this point, all expressions have been evaluated in order. + However, all expression values have been popped when evaluated, + which means we have to recover the last expression value. This is + the last value removed by means of a `drop' instruction. Instead + of adding code to inhibit dropping the last expression value, it + is here recovered by undoing the `drop'. Since `drop' is + equivalent to `adjustackSI [1]', it can be undone with `adjstackSI + [-1]'. */ + + bc_adjust_stack (-1); + + if (!last_expr_type) + last_expr_type = void_type_node; + + t = make_node (RTL_EXPR); + TREE_TYPE (t) = last_expr_type; + RTL_EXPR_RTL (t) = NULL; + RTL_EXPR_SEQUENCE (t) = NULL; + + /* Don't consider deleting this expr or containing exprs at tree level. */ + TREE_THIS_VOLATILE (t) = 1; + + last_expr_type = 0; + return t; + } + OK_DEFER_POP; if (last_expr_type == 0) @@ -1849,7 +2174,10 @@ expand_start_cond (cond, exitflag) cond_stack = thiscond; nesting_stack = thiscond; - do_jump (cond, thiscond->data.cond.next_label, NULL_RTX); + if (output_bytecode) + bc_expand_start_cond (cond, exitflag); + else + do_jump (cond, thiscond->data.cond.next_label, NULL_RTX); } /* Generate RTL between then-clause and the elseif-clause @@ -1875,6 +2203,13 @@ expand_start_else () { if (cond_stack->data.cond.endif_label == 0) cond_stack->data.cond.endif_label = gen_label_rtx (); + + if (output_bytecode) + { + bc_expand_start_else (); + return; + } + emit_jump (cond_stack->data.cond.endif_label); emit_label (cond_stack->data.cond.next_label); cond_stack->data.cond.next_label = 0; /* No more _else or _elseif calls. */ @@ -1888,15 +2223,71 @@ expand_end_cond () { struct nesting *thiscond = cond_stack; - do_pending_stack_adjust (); - if (thiscond->data.cond.next_label) - emit_label (thiscond->data.cond.next_label); - if (thiscond->data.cond.endif_label) - emit_label (thiscond->data.cond.endif_label); + if (output_bytecode) + bc_expand_end_cond (); + else + { + do_pending_stack_adjust (); + if (thiscond->data.cond.next_label) + emit_label (thiscond->data.cond.next_label); + if (thiscond->data.cond.endif_label) + emit_label (thiscond->data.cond.endif_label); + } POPSTACK (cond_stack); last_expr_type = 0; } + + +/* Generate code for the start of an if-then. COND is the expression + whose truth is to be tested; if EXITFLAG is nonzero this conditional + is to be visible to exit_something. It is assumed that the caller + has pushed the previous context on the cond stack. */ +void +bc_expand_start_cond (cond, exitflag) + tree cond; + int exitflag; +{ + struct nesting *thiscond = cond_stack; + + thiscond->data.case_stmt.nominal_type = cond; + bc_expand_expr (cond); + bc_emit_bytecode (jumpifnot); + bc_emit_bytecode_labelref (thiscond->exit_label->bc_label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + +/* Generate the label for the end of an if with + no else- clause. */ +void +bc_expand_end_cond () +{ + struct nesting *thiscond = cond_stack; + + bc_emit_bytecode_labeldef (thiscond->exit_label->bc_label); +} + +/* Generate code for the start of the else- clause of + an if-then-else. */ +void +bc_expand_start_else () +{ + struct nesting *thiscond = cond_stack; + + thiscond->data.cond.endif_label = thiscond->exit_label; + thiscond->exit_label = gen_label_rtx (); + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (thiscond->exit_label->bc_label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + bc_emit_bytecode_labeldef (thiscond->data.cond.endif_label->bc_label); +} /* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this loop should be exited by `exit_something'. This is a loop for which @@ -1923,6 +2314,12 @@ expand_start_loop (exit_flag) loop_stack = thisloop; nesting_stack = thisloop; + if (output_bytecode) + { + bc_emit_bytecode_labeldef (thisloop->data.loop.start_label->bc_label); + return thisloop; + } + do_pending_stack_adjust (); emit_queue (); emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG); @@ -1951,21 +2348,54 @@ expand_start_loop_continue_elsewhere (exit_flag) void expand_loop_continue_here () { + if (output_bytecode) + { + bc_emit_bytecode_labeldef (loop_stack->data.loop.continue_label->bc_label); + return; + } do_pending_stack_adjust (); emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT); emit_label (loop_stack->data.loop.continue_label); } +/* End a loop. */ +static void +bc_expand_end_loop () +{ + struct nesting *thisloop = loop_stack; + + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (thisloop->data.loop.start_label->bc_label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + bc_emit_bytecode_labeldef (thisloop->exit_label->bc_label); + POPSTACK (loop_stack); + last_expr_type = 0; +} + + /* Finish a loop. Generate a jump back to the top and the loop-exit label. Pop the block off of loop_stack. */ void expand_end_loop () { - register rtx insn = get_last_insn (); - register rtx start_label = loop_stack->data.loop.start_label; + register rtx insn; + register rtx start_label; rtx last_test_insn = 0; int num_insns = 0; + + if (output_bytecode) + { + bc_expand_end_loop (); + return; + } + + insn = get_last_insn (); + start_label = loop_stack->data.loop.start_label; /* Mark the continue-point at the top of the loop if none elsewhere. */ if (start_label == loop_stack->data.loop.continue_label) @@ -2113,7 +2543,15 @@ expand_exit_loop_if_false (whichloop, cond) whichloop = loop_stack; if (whichloop == 0) return 0; - do_jump (cond, whichloop->data.loop.end_label, NULL_RTX); + if (output_bytecode) + { + bc_expand_expr (cond); + bc_expand_goto_internal (jumpifnot, + whichloop->exit_label->bc_label, NULL_RTX); + } + else + do_jump (cond, whichloop->data.loop.end_label, NULL_RTX); + return 1; } @@ -2176,6 +2614,12 @@ expand_null_return () struct nesting *block = block_stack; rtx last_insn = 0; + if (output_bytecode) + { + bc_emit_instruction (ret); + return; + } + /* Does any pending block have cleanups? */ while (block && block->data.block.cleanups == 0) @@ -2298,6 +2742,15 @@ expand_return (retval) int cleanups; struct nesting *block; + /* Bytecode returns are quite simple, just leave the result on the + arithmetic stack. */ + if (output_bytecode) + { + bc_expand_expr (retval); + bc_emit_instruction (ret); + return; + } + /* If function wants no value, give it none. */ if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE) { @@ -2536,8 +2989,10 @@ expand_start_bindings (exit_flag) int exit_flag; { struct nesting *thisblock = ALLOC_NESTING (); + rtx note; - rtx note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); + if (!output_bytecode) + note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); /* Make an entry on block_stack for the block we are entering. */ @@ -2580,8 +3035,11 @@ expand_start_bindings (exit_flag) block_stack = thisblock; nesting_stack = thisblock; - /* Make a new level for allocating stack slots. */ - push_temp_slots (); + if (!output_bytecode) + { + /* Make a new level for allocating stack slots. */ + push_temp_slots (); + } } /* Given a pointer to a BLOCK node, save a pointer to the most recently @@ -2614,6 +3072,12 @@ expand_end_bindings (vars, mark_ends, dont_jump_in) register struct nesting *thisblock = block_stack; register tree decl; + if (output_bytecode) + { + bc_expand_end_bindings (vars, mark_ends, dont_jump_in); + return; + } + if (warn_unused) for (decl = vars; decl; decl = TREE_CHAIN (decl)) if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL @@ -2830,6 +3294,35 @@ expand_end_bindings (vars, mark_ends, dont_jump_in) /* Pop the stack slot nesting and free any slots at this level. */ pop_temp_slots (); } + + +/* End a binding contour. + VARS is the chain of VAR_DECL nodes for the variables bound + in this contour. MARK_ENDS is nonzer if we should put a note + at the beginning and end of this binding contour. + DONT_JUMP_IN is nonzero if it is not valid to jump into this + contour. */ + +void +bc_expand_end_bindings (vars, mark_ends, dont_jump_in) + tree vars; + int mark_ends; + int dont_jump_in; +{ + struct nesting *thisbind = nesting_stack; + tree decl; + + if (warn_unused) + for (decl = vars; decl; decl = TREE_CHAIN (decl)) + if (! TREE_USED (TREE_VALUE (decl)) && TREE_CODE (TREE_VALUE (decl)) == VAR_DECL) + warning_with_decl (decl, "unused variable `%s'"); + + bc_emit_bytecode_labeldef (thisbind->exit_label->bc_label); + + /* Pop block/bindings off stack */ + POPSTACK (nesting_stack); + POPSTACK (block_stack); +} /* Generate RTL for the automatic variable declaration DECL. (Other kinds of declarations are simply ignored if seen here.) @@ -2854,7 +3347,15 @@ expand_decl (decl) register tree decl; { struct nesting *thisblock = block_stack; - tree type = TREE_TYPE (decl); + tree type; + + if (output_bytecode) + { + bc_expand_decl (decl, 0); + return; + } + + type = TREE_TYPE (decl); /* Only automatic variables need any expansion done. Static and external variables, and external functions, @@ -3046,6 +3547,52 @@ expand_decl (decl) if (obey_regdecls) use_variable (DECL_RTL (decl)); } + + +/* Generate code for the automatic variable declaration DECL. For + most variables this just means we give it a stack offset. The + compiler sometimes emits cleanups without variables and we will + have to deal with those too. */ + +void +bc_expand_decl (decl, cleanup) + tree decl; + tree cleanup; +{ + tree type; + + if (!decl) + { + /* A cleanup with no variable. */ + if (!cleanup) + abort (); + + return; + } + + /* Only auto variables need any work. */ + if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl) || DECL_EXTERNAL (decl)) + return; + + type = TREE_TYPE (decl); + + if (type == error_mark_node) + DECL_RTL (decl) = bc_gen_rtx ((char *) 0, 0, (struct bc_label *) 0); + + else if (DECL_SIZE (decl) == 0) + + /* Variable with incomplete type. The stack offset herein will be + fixed later in expand_decl_init (). */ + DECL_RTL (decl) = bc_gen_rtx ((char *) 0, 0, (struct bc_label *) 0); + + else if (TREE_CONSTANT (DECL_SIZE (decl))) + { + DECL_RTL (decl) = bc_allocate_local (TREE_INT_CST_LOW (DECL_SIZE (decl)) / BITS_PER_UNIT, + DECL_ALIGN (decl)); + } + else + DECL_RTL (decl) = bc_allocate_variable_array (DECL_SIZE (decl)); +} /* Emit code to perform the initialization of a declaration DECL. */ @@ -3083,6 +3630,82 @@ expand_decl_init (decl) free_temp_slots (); } +/* Expand initialization for variable-sized types. Allocate array + using newlocalSI and set local variable, which is a pointer to the + storage. */ + +bc_expand_variable_local_init (decl) + tree decl; +{ + /* Evaluate size expression and coerce to SI */ + bc_expand_expr (DECL_SIZE (decl)); + + /* Type sizes are always (?) of TREE_CODE INTEGER_CST, so + no coercion is necessary (?) */ + +/* emit_typecode_conversion (preferred_typecode (TYPE_MODE (DECL_SIZE (decl)), + TREE_UNSIGNED (DECL_SIZE (decl))), SIcode); */ + + /* Emit code to allocate array */ + bc_emit_instruction (newlocalSI); + + /* Store array pointer in local variable. This is the only instance + where we actually want the address of the pointer to the + variable-size block, rather than the pointer itself. We avoid + using expand_address() since that would cause the pointer to be + pushed rather than its address. Hence the hard-coded reference; + notice also that the variable is always local (no global + variable-size type variables). */ + + bc_load_localaddr (DECL_RTL (decl)); + bc_emit_instruction (storeP); +} + + +/* Emit code to initialize a declaration. */ +void +bc_expand_decl_init (decl) + tree decl; +{ + int org_stack_depth; + + /* Statical initializers are handled elsewhere */ + + if (TREE_STATIC (decl)) + return; + + /* Memory original stack depth */ + org_stack_depth = stack_depth; + + /* If the type is variable-size, we first create its space (we ASSUME + it CAN'T be static). We do this regardless of whether there's an + initializer assignment or not. */ + + if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + bc_expand_variable_local_init (decl); + + /* Expand initializer assignment */ + if (DECL_INITIAL (decl) == error_mark_node) + { + enum tree_code code = TREE_CODE (TREE_TYPE (decl)); + + if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE + || code == POINTER_TYPE) + + expand_assignment (TREE_TYPE (decl), decl, + convert (TREE_TYPE (decl), integer_zero_node)); + } + else if (DECL_INITIAL (decl)) + expand_assignment (TREE_TYPE (decl), decl, DECL_INITIAL (decl)); + + /* Restore stack depth */ + if (org_stack_depth > stack_depth) + abort (); + + bc_adjust_stack (stack_depth - org_stack_depth); +} + + /* CLEANUP is an expression to be executed at exit from this binding contour; for example, in C++, it might call the destructor for this variable. @@ -3301,6 +3924,12 @@ expand_start_case (exit_flag, expr, type, printname) case_stack = thiscase; nesting_stack = thiscase; + if (output_bytecode) + { + bc_expand_start_case (thiscase, expr, type, printname); + return; + } + do_pending_stack_adjust (); /* Make sure case_stmt.start points to something that won't @@ -3311,6 +3940,32 @@ expand_start_case (exit_flag, expr, type, printname) thiscase->data.case_stmt.start = get_last_insn (); } + +/* Enter a case statement. It is assumed that the caller has pushed + the current context onto the case stack. */ +void +bc_expand_start_case (thiscase, expr, type, printname) + struct nesting *thiscase; + tree expr; + tree type; + char *printname; +{ + bc_expand_expr (expr); + bc_expand_conversion (TREE_TYPE (expr), type); + + /* For cases, the skip is a place we jump to that's emitted after + the size of the jump table is known. */ + + thiscase->data.case_stmt.skip_label = gen_label_rtx (); + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (thiscase->data.case_stmt.skip_label->bc_label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + + /* Start a "dummy case statement" within which case labels are invalid and are not connected to any larger real case statement. This can be used if you don't want to let a case statement jump @@ -3382,6 +4037,9 @@ pushcase (value, converter, label, duplicate) tree index_type; tree nominal_type; + if (output_bytecode) + return bc_pushcase (value, label); + /* Fail if not inside a real case statement. */ if (! (case_stack && case_stack->data.case_stmt.start)) return 1; @@ -3588,6 +4246,62 @@ pushcase_range (value1, value2, converter, label, duplicate) return 0; } + + +/* Accumulate one case or default label; VALUE is the value of the + case, or nil for a default label. If not currently inside a case, + return 1 and do nothing. If VALUE is a duplicate or overlaps, return + 2 and do nothing. If VALUE is out of range, return 3 and do nothing. + Return 0 on success. This function is a leftover from the earlier + bytecode compiler, which was based on gcc 1.37. It should be + merged into pushcase. */ + +int +bc_pushcase (value, label) + tree value; + tree label; +{ + struct nesting *thiscase = case_stack; + struct case_node *case_label, *new_label; + + if (! thiscase) + return 1; + + /* Fail if duplicate, overlap, or out of type range. */ + if (value) + { + value = convert (thiscase->data.case_stmt.nominal_type, value); + if (! int_fits_type_p (value, thiscase->data.case_stmt.nominal_type)) + return 3; + + for (case_label = thiscase->data.case_stmt.case_list; + case_label->left; case_label = case_label->left) + if (! tree_int_cst_lt (case_label->left->high, value)) + break; + + if (case_label != thiscase->data.case_stmt.case_list + && ! tree_int_cst_lt (case_label->high, value) + || case_label->left && ! tree_int_cst_lt (value, case_label->left->low)) + return 2; + + new_label = (struct case_node *) oballoc (sizeof (struct case_node)); + new_label->low = new_label->high = copy_node (value); + new_label->code_label = label; + new_label->left = case_label->left; + + case_label->left = new_label; + thiscase->data.case_stmt.num_ranges++; + } + else + { + if (thiscase->data.case_stmt.default_label) + return 2; + thiscase->data.case_stmt.default_label = label; + } + + expand_label (label); + return 0; +} /* Called when the index of a switch statement is an enumerated type and there is no default label. @@ -3609,6 +4323,12 @@ check_for_full_enumeration_handling (type) register tree chain; int all_values = 1; + if (output_bytecode) + { + bc_check_for_full_enumeration_handling (type); + return; + } + /* The time complexity of this loop is currently O(N * M), with N being the number of members in the enumerated type, and M being the number of case expressions in the switch. */ @@ -3707,6 +4427,46 @@ check_for_full_enumeration_handling (type) } #endif /* 0 */ } + + +/* Check that all enumeration literals are covered by the case + expressions of a switch. Also warn if there are any cases + that are not elements of the enumerated type. */ +void +bc_check_for_full_enumeration_handling (type) + tree type; +{ + struct nesting *thiscase = case_stack; + struct case_node *c; + tree e; + + /* Check for enums not handled. */ + for (e = TYPE_VALUES (type); e; e = TREE_CHAIN (e)) + { + for (c = thiscase->data.case_stmt.case_list->left; + c && tree_int_cst_lt (c->high, TREE_VALUE (e)); + c = c->left) + ; + if (! (c && tree_int_cst_equal (c->low, TREE_VALUE (e)))) + warning ("enumerated value `%s' not handled in switch", + IDENTIFIER_POINTER (TREE_PURPOSE (e))); + } + + /* Check for cases not in the enumeration. */ + for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left) + { + for (e = TYPE_VALUES (type); + e && !tree_int_cst_equal (c->low, TREE_VALUE (e)); + e = TREE_CHAIN (e)) + ; + if (! e) + warning ("case value `%d' not in enumerated type `%s'", + TREE_INT_CST_LOW (c->low), + IDENTIFIER_POINTER (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE + ? TYPE_NAME (type) + : DECL_NAME (TYPE_NAME (type)))); + } +} /* Terminate a case (Pascal) or switch (C) statement in which ORIG_INDEX is the expression to be tested. @@ -3721,14 +4481,24 @@ expand_end_case (orig_index) register struct case_node *n; int count; rtx index; - rtx table_label = gen_label_rtx (); + rtx table_label; int ncases; rtx *labelvec; register int i; rtx before_case; register struct nesting *thiscase = case_stack; - tree index_expr = thiscase->data.case_stmt.index_expr; - int unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr)); + tree index_expr; + int unsignedp; + + if (output_bytecode) + { + bc_expand_end_case (orig_index); + return; + } + + table_label = gen_label_rtx (); + index_expr = thiscase->data.case_stmt.index_expr; + unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr)); do_pending_stack_adjust (); @@ -4069,6 +4839,110 @@ expand_end_case (orig_index) free_temp_slots (); } + +/* Terminate a case statement. EXPR is the original index + expression. */ +void +bc_expand_end_case (expr) + tree expr; +{ + struct nesting *thiscase = case_stack; + enum bytecode_opcode opcode; + struct bc_label *jump_label; + struct case_node *c; + + bc_emit_bytecode (jump); + bc_emit_bytecode_labelref (thiscase->exit_label->bc_label); + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + /* Now that the size of the jump table is known, emit the actual + indexed jump instruction. */ + bc_emit_bytecode_labeldef (thiscase->data.case_stmt.skip_label->bc_label); + + opcode = TYPE_MODE (thiscase->data.case_stmt.nominal_type) == SImode + ? TREE_UNSIGNED (thiscase->data.case_stmt.nominal_type) ? caseSU : caseSI + : TREE_UNSIGNED (thiscase->data.case_stmt.nominal_type) ? caseDU : caseDI; + + bc_emit_bytecode (opcode); + + /* Now emit the case instructions literal arguments, in order. + In addition to the value on the stack, it uses: + 1. The address of the jump table. + 2. The size of the jump table. + 3. The default label. */ + + jump_label = bc_get_bytecode_label (); + bc_emit_bytecode_labelref (jump_label); + bc_emit_bytecode_const ((char *) &thiscase->data.case_stmt.num_ranges, + sizeof thiscase->data.case_stmt.num_ranges); + + if (thiscase->data.case_stmt.default_label) + bc_emit_bytecode_labelref (DECL_RTL (thiscase-> + data.case_stmt.default_label)->bc_label); + else + bc_emit_bytecode_labelref (thiscase->exit_label->bc_label); + + /* Output the jump table. */ + + bc_align_bytecode (3 /* PTR_ALIGN */); + bc_emit_bytecode_labeldef (jump_label); + + if (TYPE_MODE (thiscase->data.case_stmt.nominal_type) == SImode) + for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left) + { + opcode = TREE_INT_CST_LOW (c->low); + bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); + + opcode = TREE_INT_CST_LOW (c->high); + bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); + + bc_emit_bytecode_labelref (DECL_RTL (c->code_label)->bc_label); + } + else + if (TYPE_MODE (thiscase->data.case_stmt.nominal_type) == DImode) + for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left) + { + bc_emit_bytecode_DI_const (c->low); + bc_emit_bytecode_DI_const (c->high); + + bc_emit_bytecode_labelref (DECL_RTL (c->code_label)->bc_label); + } + else + /* Bad mode */ + abort (); + + + bc_emit_bytecode_labeldef (thiscase->exit_label->bc_label); + + /* Possibly issue enumeration warnings. */ + + if (!thiscase->data.case_stmt.default_label + && TREE_CODE (TREE_TYPE (expr)) == ENUMERAL_TYPE + && TREE_CODE (expr) != INTEGER_CST + && warn_switch) + check_for_full_enumeration_handling (TREE_TYPE (expr)); + + +#ifdef DEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif + + POPSTACK (case_stack); +} + + +/* Return unique bytecode ID. */ +int +bc_new_uid () +{ + static int bc_uid = 0; + + return (++bc_uid); +} + /* Generate code to jump to LABEL if OP1 and OP2 are equal. */ static void diff --git a/gcc/toplev.c b/gcc/toplev.c index 498610f..94e4280 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -57,6 +57,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" #endif + +#include "bytecode.h" +#include "bc-emit.h" #ifdef VMS /* The extra parameters substantially improve the I/O performance. */ @@ -211,6 +214,9 @@ int errorcount = 0; int warningcount = 0; int sorrycount = 0; +/* Flag to output bytecode instead of native assembler */ +int output_bytecode = 0; + /* Pointer to function to compute the name to use to print a declaration. */ char *(*decl_printable_name) (); @@ -515,6 +521,7 @@ struct { char *string; int *variable; int on_value;} f_options[] = {"inhibit-size-directive", &flag_inhibit_size_directive, 1}, {"verbose-asm", &flag_verbose_asm, 1}, {"gnu-linker", &flag_gnu_linker, 1} + {"bytecode", &output_bytecode, 1} }; /* Table of language-specific options. */ @@ -885,11 +892,14 @@ void fatal_insn_not_found (insn) rtx insn; { - if (INSN_CODE (insn) < 0) - error ("internal error--unrecognizable insn:", 0); - else - error ("internal error--insn does not satisfy its constraints:", 0); - debug_rtx (insn); + if (!output_bytecode) + { + if (INSN_CODE (insn) < 0) + error ("internal error--unrecognizable insn:", 0); + else + error ("internal error--insn does not satisfy its constraints:", 0); + debug_rtx (insn); + } if (asm_out_file) fflush (asm_out_file); if (aux_info_file) @@ -1585,6 +1595,8 @@ compile_file (name) init_obstacks (); init_tree_codes (); init_lex (); + /* Some of these really don't need to be called when generating bytecode, + but the options would have to be parsed first to know that. -bson */ init_rtl (); init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL || debug_info_level == DINFO_LEVEL_VERBOSE); @@ -1813,34 +1825,51 @@ compile_file (name) input_file_stack->next = 0; input_file_stack->name = input_filename; - ASM_FILE_START (asm_out_file); + if (!output_bytecode) + { + ASM_FILE_START (asm_out_file); + } - /* Output something to inform GDB that this compilation was by GCC. */ + /* Output something to inform GDB that this compilation was by GCC. Also + serves to tell GDB file consists of bytecodes. */ + if (output_bytecode) + fprintf (asm_out_file, "bc_gcc2_compiled.:\n"); + else + { #ifndef ASM_IDENTIFY_GCC - fprintf (asm_out_file, "gcc2_compiled.:\n"); + fprintf (asm_out_file, "gcc2_compiled.:\n"); #else - ASM_IDENTIFY_GCC (asm_out_file); + ASM_IDENTIFY_GCC (asm_out_file); #endif + } /* Output something to identify which front-end produced this file. */ #ifdef ASM_IDENTIFY_LANGUAGE ASM_IDENTIFY_LANGUAGE (asm_out_file); #endif -/* ??? Note: There used to be a conditional here - to call assemble_zeros without fail if DBX_DEBUGGING_INFO is defined. - This was to guarantee separation between gcc_compiled. and - the first function, for the sake of dbx on Suns. - However, having the extra zero here confused the Emacs - code for unexec, and might confuse other programs too. - Therefore, I took out that change. - In future versions we should find another way to solve - that dbx problem. -- rms, 23 May 93. */ - - /* Don't let the first function fall at the same address - as gcc_compiled., if profiling. */ - if (profile_flag || profile_block_flag) - assemble_zeros (UNITS_PER_WORD); + if (output_bytecode) + { + if (profile_flag || profile_block_flag) + error ("profiling not supported in bytecode compilation"); + } + else + { + /* ??? Note: There used to be a conditional here + to call assemble_zeros without fail if DBX_DEBUGGING_INFO is defined. + This was to guarantee separation between gcc_compiled. and + the first function, for the sake of dbx on Suns. + However, having the extra zero here confused the Emacs + code for unexec, and might confuse other programs too. + Therefore, I took out that change. + In future versions we should find another way to solve + that dbx problem. -- rms, 23 May 93. */ + + /* Don't let the first function fall at the same address + as gcc_compiled., if profiling. */ + if (profile_flag || profile_block_flag) + assemble_zeros (UNITS_PER_WORD); + } /* If dbx symbol table desired, initialize writing it and output the predefined types. */ @@ -1861,7 +1890,8 @@ compile_file (name) /* Initialize yet another pass. */ - init_final (main_input_filename); + if (!output_bytecode) + init_final (main_input_filename); start_time = get_run_time (); @@ -2031,11 +2061,14 @@ compile_file (name) /* Output some stuff at end of file if nec. */ - end_final (main_input_filename); + if (!output_bytecode) + { + end_final (main_input_filename); #ifdef ASM_FILE_END - ASM_FILE_END (asm_out_file); + ASM_FILE_END (asm_out_file); #endif + } after_finish_compilation: @@ -2113,24 +2146,28 @@ compile_file (name) { fprintf (stderr,"\n"); print_time ("parse", parse_time); - print_time ("integration", integration_time); - print_time ("jump", jump_time); - print_time ("cse", cse_time); - print_time ("loop", loop_time); - print_time ("cse2", cse2_time); - print_time ("flow", flow_time); - print_time ("combine", combine_time); - print_time ("sched", sched_time); - print_time ("local-alloc", local_alloc_time); - print_time ("global-alloc", global_alloc_time); - print_time ("sched2", sched2_time); - print_time ("dbranch", dbr_sched_time); - print_time ("shorten-branch", shorten_branch_time); - print_time ("stack-reg", stack_reg_time); - print_time ("final", final_time); - print_time ("varconst", varconst_time); - print_time ("symout", symout_time); - print_time ("dump", dump_time); + + if (!output_bytecode) + { + print_time ("integration", integration_time); + print_time ("jump", jump_time); + print_time ("cse", cse_time); + print_time ("loop", loop_time); + print_time ("cse2", cse2_time); + print_time ("flow", flow_time); + print_time ("combine", combine_time); + print_time ("sched", sched_time); + print_time ("local-alloc", local_alloc_time); + print_time ("global-alloc", global_alloc_time); + print_time ("sched2", sched2_time); + print_time ("dbranch", dbr_sched_time); + print_time ("shorten-branch", shorten_branch_time); + print_time ("stack-reg", stack_reg_time); + print_time ("final", final_time); + print_time ("varconst", varconst_time); + print_time ("symout", symout_time); + print_time ("dump", dump_time); + } } } @@ -2236,6 +2273,9 @@ rest_of_compilation (decl) tree saved_arguments = 0; int failure = 0; + if (output_bytecode) + return; + /* If we are reconsidering an inline function at the end of compilation, skip the stuff for making it inline. */ @@ -3166,7 +3206,12 @@ main (argc, argv, envp) error ("Invalid option `%s'", argv[i]); } else if (!strcmp (str, "p")) - profile_flag = 1; + { + if (!output_bytecode) + profile_flag = 1; + else + error ("profiling not supported in bytecode compilation"); + } else if (!strcmp (str, "a")) { #if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER) @@ -3325,6 +3370,18 @@ You Lose! You must define PREFERRED_DEBUGGING_TYPE! filename = argv[i]; } + /* Initialize for bytecode output. A good idea to do this as soon as + possible after the "-f" options have been parsed. */ + if (output_bytecode) + { +#ifndef TARGET_SUPPORTS_BYTECODE + /* Just die with a fatal error if not supported */ + fatal ("-fbytecode can not be used for this target"); +#else + bc_initialize (); +#endif + } + if (optimize == 0) { /* Inlining does not work if not optimizing, @@ -3398,10 +3455,14 @@ You Lose! You must define PREFERRED_DEBUGGING_TYPE! } /* Now that register usage is specified, convert it to HARD_REG_SETs. */ - init_reg_sets_1 (); + if (!output_bytecode) + init_reg_sets_1 (); compile_file (filename); + if (output_bytecode) + bc_write_file (stdout); + #ifndef OS2 #ifndef VMS if (flag_print_mem) diff --git a/gcc/varasm.c b/gcc/varasm.c index 66128bb..7d16f36 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -38,6 +38,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "regs.h" #include "defaults.h" #include "real.h" +#include "bytecode.h" #include "obstack.h" @@ -96,9 +97,11 @@ void assemble_name (); int output_addressed_constants (); void output_constant (); void output_constructor (); +void output_byte_asm (); void text_section (); void readonly_data_section (); void data_section (); +static void bc_assemble_integer (); #ifdef EXTRA_SECTIONS static enum in_section {no_section, in_text, in_data, EXTRA_SECTIONS} in_section @@ -120,7 +123,11 @@ text_section () { if (in_section != in_text) { - fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); + if (output_bytecode) + bc_text (); + else + fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); + in_section = in_text; } } @@ -132,16 +139,21 @@ data_section () { if (in_section != in_data) { - if (flag_shared_data) + if (output_bytecode) + bc_data (); + else { + if (flag_shared_data) + { #ifdef SHARED_SECTION_ASM_OP - fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); + fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); #else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); #endif + } + else + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); } - else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); in_section = in_data; } @@ -178,6 +190,16 @@ make_function_rtl (decl) { char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + if (output_bytecode) + { + if (DECL_RTL (decl) == 0) + DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0); + + /* Record that at least one function has been defined. */ + function_defined = 1; + return; + } + /* Rename a nested function to avoid conflicts. */ if (decl_function_context (decl) != 0 && DECL_INITIAL (decl) != 0 @@ -211,6 +233,48 @@ make_function_rtl (decl) function_defined = 1; } +/* Create the DECL_RTL for a declaration for a static or external + variable or static or external function. + ASMSPEC, if not 0, is the string which the user specified + as the assembler symbol name. + TOP_LEVEL is nonzero if this is a file-scope variable. + This is never called for PARM_DECLs. */ +void +bc_make_decl_rtl (decl, asmspec, top_level) + tree decl; + char *asmspec; + int top_level; +{ + register char *name = TREE_STRING_POINTER (DECL_ASSEMBLER_NAME (decl)); + + if (DECL_RTL (decl) == 0) + { + /* Print an error message for register variables. */ + if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL) + error ("function declared `register'"); + else if (DECL_REGISTER (decl)) + error ("global register variables not supported in the interpreter"); + + /* Handle ordinary static variables and functions. */ + if (DECL_RTL (decl) == 0) + { + /* Can't use just the variable's own name for a variable + whose scope is less than the whole file. + Concatenate a distinguishing number. */ + if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0) + { + char *label; + + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0); + } + } +} + /* Given NAME, a putative register name, discard any customary prefixes. */ static char * @@ -301,7 +365,15 @@ make_decl_rtl (decl, asmspec, top_level) int top_level; { register char *name; - int reg_number = decode_reg_name (asmspec); + int reg_number; + + if (output_bytecode) + { + bc_make_decl_rtl (decl, asmspec, top_level); + return; + } + + reg_number = decode_reg_name (asmspec); if (DECL_ASSEMBLER_NAME (decl) != NULL_TREE) name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); @@ -465,6 +537,12 @@ void assemble_asm (string) tree string; { + if (output_bytecode) + { + error ("asm statements not allowed in interpreter"); + return; + } + app_enable (); if (TREE_CODE (string) == ADDR_EXPR) @@ -576,7 +654,12 @@ assemble_start_function (decl, fnname) /* Tell assembler to move to target machine's alignment for functions. */ align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); if (align > 0) - ASM_OUTPUT_ALIGN (asm_out_file, align); + { + if (output_bytecode) + BC_OUTPUT_ALIGN (asm_out_file, align); + else + ASM_OUTPUT_ALIGN (asm_out_file, align); + } #ifdef ASM_OUTPUT_FUNCTION_PREFIX ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname); @@ -600,7 +683,10 @@ assemble_start_function (decl, fnname) { if (!first_global_object_name) STRIP_NAME_ENCODING (first_global_object_name, fnname); - ASM_GLOBALIZE_LABEL (asm_out_file, fnname); + if (output_bytecode) + BC_GLOBALIZE_LABEL (asm_out_file, fnname); + else + ASM_GLOBALIZE_LABEL (asm_out_file, fnname); } /* Do any machine/system dependent processing of the function name */ @@ -608,7 +694,10 @@ assemble_start_function (decl, fnname) ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); #else /* Standard thing is just output label for the function. */ - ASM_OUTPUT_LABEL (asm_out_file, fnname); + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, fnname); + else + ASM_OUTPUT_LABEL (asm_out_file, fnname); #endif /* ASM_DECLARE_FUNCTION_NAME */ } @@ -631,6 +720,12 @@ void assemble_zeros (size) int size; { + if (output_bytecode) + { + bc_emit_const_skip (size); + return; + } + #ifdef ASM_NO_SKIP_IN_TEXT /* The `space' pseudo in the text section outputs nop insns rather than 0s, so we must output 0s explicitly in the text section. */ @@ -664,7 +759,12 @@ assemble_zeros (size) else #endif if (size > 0) - ASM_OUTPUT_SKIP (asm_out_file, size); + { + if (output_bytecode) + BC_OUTPUT_SKIP (asm_out_file, size); + else + ASM_OUTPUT_SKIP (asm_out_file, size); + } } /* Assemble an alignment pseudo op for an ALIGN-bit boundary. */ @@ -688,6 +788,12 @@ assemble_string (p, size) int pos = 0; int maximum = 2000; + if (output_bytecode) + { + bc_emit (p, size); + return; + } + /* If the string is very long, split it up. */ while (pos < size) @@ -696,7 +802,10 @@ assemble_string (p, size) if (thissize > maximum) thissize = maximum; - ASM_OUTPUT_ASCII (asm_out_file, p, thissize); + if (output_bytecode) + BC_OUTPUT_ASCII (asm_out_file, p, thissize); + else + ASM_OUTPUT_ASCII (asm_out_file, p, thissize); pos += thissize; p += thissize; @@ -725,6 +834,9 @@ assemble_variable (decl, top_level, at_end, dont_output_data) int reloc = 0; enum in_section saved_in_section; + if (output_bytecode) + return; + if (GET_CODE (DECL_RTL (decl)) == REG) { /* Do output symbol info for global register variables, but do nothing @@ -734,19 +846,22 @@ assemble_variable (decl, top_level, at_end, dont_output_data) return; TREE_ASM_WRITTEN (decl) = 1; + if (!output_bytecode) + { #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - /* File-scope global variables are output here. */ - if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - && top_level) - dbxout_symbol (decl, 0); + /* File-scope global variables are output here. */ + if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + && top_level) + dbxout_symbol (decl, 0); #endif #ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && top_level - /* Leave initialized global vars for end of compilation; - see comment in compile_file. */ - && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) - sdbout_symbol (decl, 0); + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); #endif + } /* Don't output any DWARF debugging information for variables here. In the case of local variables, the information for them is output @@ -880,12 +995,17 @@ assemble_variable (decl, top_level, at_end, dont_output_data) ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded); else #endif + if (output_bytecode) + BC_OUTPUT_COMMON (asm_out_file, name, size, rounded); + else + { #ifdef ASM_OUTPUT_ALIGNED_COMMON - ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, - DECL_ALIGN (decl)); + ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, + DECL_ALIGN (decl)); #else - ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); + ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); #endif + } } else { @@ -894,12 +1014,17 @@ assemble_variable (decl, top_level, at_end, dont_output_data) ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); else #endif + if (output_bytecode) + BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + else + { #ifdef ASM_OUTPUT_ALIGNED_LOCAL - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, - DECL_ALIGN (decl)); + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, + DECL_ALIGN (decl)); #else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); #endif + } } goto finish; } @@ -1017,14 +1142,22 @@ assemble_variable (decl, top_level, at_end, dont_output_data) DECL_ALIGN (decl) = align; if (align > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + { + if (output_bytecode) + BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + else + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + } /* Do any machine/system dependent processing of the object. */ #ifdef ASM_DECLARE_OBJECT_NAME ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl); #else /* Standard thing is just output label for the object. */ - ASM_OUTPUT_LABEL (asm_out_file, name); + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, name); + else + ASM_OUTPUT_LABEL (asm_out_file, name); #endif /* ASM_DECLARE_OBJECT_NAME */ if (!dont_output_data) @@ -1110,6 +1243,55 @@ contains_pointers_p (type) } } +/* Output text storage for constructor CONSTR. Returns rtx of + storage. */ + +rtx +bc_output_constructor (constr) + tree constr; +{ + int i; + + /* Must always be a literal; non-literal constructors are handled + differently. */ + + if (!TREE_CONSTANT (constr)) + abort (); + + /* Always const */ + text_section (); + + /* Align */ + for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++); + if (i > 0) + BC_OUTPUT_ALIGN (asm_out_file, i); + + /* Output data */ + output_constant (constr, int_size_in_bytes (TREE_TYPE (constr))); +} + + +/* Create storage for constructor CONSTR. */ + +void +bc_output_data_constructor (constr) + tree constr; +{ + int i; + + /* Put in data section */ + data_section (); + + /* Align */ + for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++); + if (i > 0) + BC_OUTPUT_ALIGN (asm_out_file, i); + + /* The constructor is filled in at runtime. */ + BC_OUTPUT_SKIP (asm_out_file, int_size_in_bytes (TREE_TYPE (constr))); +} + + /* Output something to declare an external symbol to the assembler. (Most assemblers don't need this, so we normally output nothing.) Do nothing if DECL is not external. */ @@ -1118,6 +1300,9 @@ void assemble_external (decl) tree decl; { + if (output_bytecode) + return; + #ifdef ASM_OUTPUT_EXTERNAL if (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd' && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) @@ -1142,11 +1327,14 @@ assemble_external_libcall (fun) rtx fun; { #ifdef ASM_OUTPUT_EXTERNAL_LIBCALL - /* Declare library function name external when first used, if nec. */ - if (! SYMBOL_REF_USED (fun)) + if (!output_bytecode) { - SYMBOL_REF_USED (fun) = 1; - ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun); + /* Declare library function name external when first used, if nec. */ + if (! SYMBOL_REF_USED (fun)) + { + SYMBOL_REF_USED (fun) = 1; + ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun); + } } #endif } @@ -1166,7 +1354,10 @@ void assemble_label (name) char *name; { - ASM_OUTPUT_LABEL (asm_out_file, name); + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, name); + else + ASM_OUTPUT_LABEL (asm_out_file, name); } /* Output to FILE a reference to the assembler name of a C-level name NAME. @@ -1181,9 +1372,19 @@ assemble_name (file, name) char *name; { if (name[0] == '*') - fputs (&name[1], file); + { + if (output_bytecode) + bc_emit_labelref (name); + else + fputs (&name[1], file); + } else - ASM_OUTPUT_LABELREF (file, name); + { + if (output_bytecode) + BC_OUTPUT_LABELREF (file, name); + else + ASM_OUTPUT_LABELREF (file, name); + } } /* Allocate SIZE bytes writable static space with a gensym name @@ -1214,12 +1415,21 @@ assemble_static_space (size) strlen (name) + 2); strcpy (namestring, name); - x = gen_rtx (SYMBOL_REF, Pmode, namestring); + if (output_bytecode) + x = bc_gen_rtx (namestring, 0, (struct bc_label *) 0); + else + x = gen_rtx (SYMBOL_REF, Pmode, namestring); + + if (output_bytecode) + BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + else + { #ifdef ASM_OUTPUT_ALIGNED_LOCAL - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); #else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); #endif + } return x; } @@ -1234,6 +1444,10 @@ assemble_trampoline_template () char *name; int align; + /* Shouldn't get here */ + if (output_bytecode) + abort (); + /* By default, put trampoline templates in read-only data section. */ #ifdef TRAMPOLINE_SECTION @@ -1683,9 +1897,13 @@ decode_addr_const (exp, value) break; case LABEL_DECL: - x = gen_rtx (MEM, FUNCTION_MODE, - gen_rtx (LABEL_REF, VOIDmode, - label_rtx (TREE_OPERAND (exp, 0)))); + if (output_bytecode) + /* FIXME: this may not be correct, check it */ + x = bc_gen_rtx (TREE_STRING_POINTER (target), 0, (struct bc_label *) 0); + else + x = gen_rtx (MEM, FUNCTION_MODE, + gen_rtx (LABEL_REF, VOIDmode, + label_rtx (TREE_OPERAND (exp, 0)))); break; case REAL_CST: @@ -1699,9 +1917,12 @@ decode_addr_const (exp, value) abort (); } - if (GET_CODE (x) != MEM) - abort (); - x = XEXP (x, 0); + if (!output_bytecode) + { + if (GET_CODE (x) != MEM) + abort (); + x = XEXP (x, 0); + } value->base = x; value->offset = offset; @@ -2171,47 +2392,57 @@ output_constant_def (exp) to see if any of them describes EXP. If yes, the descriptor records the label number already assigned. */ - hash = const_hash (exp) % MAX_HASH_TABLE; - - for (desc = const_hash_table[hash]; desc; desc = desc->next) - if (compare_constant (exp, desc)) - { - found = desc->label; - break; - } - - if (found == 0) + if (!output_bytecode) { - /* No constant equal to EXP is known to have been output. - Make a constant descriptor to enter EXP in the hash table. - Assign the label number and record it in the descriptor for - future calls to this function to find. */ - - /* Create a string containing the label name, in LABEL. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); - - desc = record_constant (exp); - desc->next = const_hash_table[hash]; - desc->label - = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); - const_hash_table[hash] = desc; + hash = const_hash (exp) % MAX_HASH_TABLE; + + for (desc = const_hash_table[hash]; desc; desc = desc->next) + if (compare_constant (exp, desc)) + { + found = desc->label; + break; + } + + if (found == 0) + { + /* No constant equal to EXP is known to have been output. + Make a constant descriptor to enter EXP in the hash table. + Assign the label number and record it in the descriptor for + future calls to this function to find. */ + + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + + desc = record_constant (exp); + desc->next = const_hash_table[hash]; + desc->label + = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); + const_hash_table[hash] = desc; + } + else + { + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + } } - + /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ push_obstacks_nochange (); if (TREE_PERMANENT (exp)) end_temporary_allocation (); - def = gen_rtx (SYMBOL_REF, Pmode, desc->label); - - TREE_CST_RTL (exp) - = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); - RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) - MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1; - + if (!output_bytecode) + { + def = gen_rtx (SYMBOL_REF, Pmode, desc->label); + + TREE_CST_RTL (exp) + = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); + RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1; + } pop_obstacks (); /* Optionally set flags or add text to the name to record information @@ -2283,7 +2514,12 @@ output_constant_def_contents (exp, reloc, labelno) #endif if (align > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + { + if (!output_bytecode) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + else + BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + } /* Output the label itself. */ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", labelno); @@ -2891,6 +3127,22 @@ output_addressed_constants (exp) } return reloc; } + + +/* Output assembler for byte constant */ +void +output_byte_asm (byte) + int byte; +{ + if (output_bytecode) + bc_emit_const ((char *) &byte, sizeof (char)); +#ifdef ASM_OUTPUT_BYTE + else + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + } +#endif +} /* Output assembler code for constant EXP to FILE, with no label. This includes the pseudo-op such as ".int" or ".byte", and a newline. @@ -2925,7 +3177,10 @@ output_constant (exp, size) This means to fill the space with zeros. */ if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0) { - assemble_zeros (size); + if (output_bytecode) + bc_emit_const_skip (size); + else + assemble_zeros (size); return; } @@ -3005,6 +3260,101 @@ output_constant (exp, size) if (size > 0) assemble_zeros (size); } + + +/* Bytecode specific code to output assembler for integer. */ +void +bc_assemble_integer (exp, size) + tree exp; + int size; +{ + tree const_part; + tree addr_part; + tree tmp; + + /* FIXME: is this fold() business going to be as good as the + expand_expr() using EXPAND_SUM above in the RTL case? I + hate RMS. + FIXME: Copied as is from BC-GCC1; may need work. Don't hate. -bson */ + + exp = fold (exp); + + while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR) + exp = TREE_OPERAND (exp, 0); + if (TREE_CODE (exp) == INTEGER_CST) + { + const_part = exp; + addr_part = 0; + } + else if (TREE_CODE (exp) == PLUS_EXPR) + { + const_part = TREE_OPERAND (exp, 0); + while (TREE_CODE (const_part) == NOP_EXPR + || TREE_CODE (const_part) == CONVERT_EXPR) + const_part = TREE_OPERAND (const_part, 0); + addr_part = TREE_OPERAND (exp, 1); + while (TREE_CODE (addr_part) == NOP_EXPR + || TREE_CODE (addr_part) == CONVERT_EXPR) + addr_part = TREE_OPERAND (addr_part, 0); + if (TREE_CODE (const_part) != INTEGER_CST) + tmp = const_part, const_part = addr_part, addr_part = tmp; + if (TREE_CODE (const_part) != INTEGER_CST + || TREE_CODE (addr_part) != ADDR_EXPR) + abort (); /* FIXME: we really haven't considered + all the possible cases here. */ + } + else if (TREE_CODE (exp) == ADDR_EXPR) + { + const_part = integer_zero_node; + addr_part = exp; + } + else + abort (); /* FIXME: ditto previous. */ + + if (addr_part == 0) + { + if (size == 1) + { + char c = TREE_INT_CST_LOW (const_part); + bc_emit (&c, 1); + size -= 1; + } + else if (size == 2) + { + short s = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &s, 2); + size -= 2; + } + else if (size == 4) + { + int i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); + size -= 4; + } + else if (size == 8) + { +#if WORDS_BIG_ENDIAN + int i = TREE_INT_CST_HIGH (const_part); + bc_emit ((char *) &i, 4); + i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); +#else + int i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); + i = TREE_INT_CST_HIGH (const_part); + bc_emit ((char *) &i, 4); +#endif + size -= 8; + } + } + else + if (size == 4 + && TREE_CODE (TREE_OPERAND (addr_part, 0)) == VAR_DECL) + bc_emit_labelref (DECL_ASSEMBLER_NAME (TREE_OPERAND (addr_part, 0)), + TREE_INT_CST_LOW (const_part)); + else + abort (); /* FIXME: there may be more cases. */ +} /* Subroutine of output_constant, used for CONSTRUCTORs (aggregate constants). @@ -3083,7 +3433,10 @@ output_constructor (exp, size) if each element has the proper size. */ if ((field != 0 || index != 0) && bitpos != total_bytes) { - assemble_zeros (bitpos - total_bytes); + if (!output_bytecode) + assemble_zeros (bitpos - total_bytes); + else + bc_emit_const_skip (bitpos - total_bytes); total_bytes = bitpos; } @@ -3254,3 +3607,42 @@ output_constructor (exp, size) if (total_bytes < size) assemble_zeros (size - total_bytes); } + + +/* Output asm to handle ``#pragma weak'' */ +void +handle_pragma_weak (what, asm_out_file, name, value) + enum pragma_state what; + FILE *asm_out_file; + char *name, *value; +{ + if (what == ps_name || what == ps_value) + { + fprintf (asm_out_file, "\t%s\t", WEAK_ASM_OP); + + if (output_bytecode) + BC_OUTPUT_LABELREF (asm_out_file, name); + else + ASM_OUTPUT_LABELREF (asm_out_file, name); + + fputc ('\n', asm_out_file); + if (what == ps_value) + { + fprintf (asm_out_file, "\t%s\t", SET_ASM_OP); + if (output_bytecode) + BC_OUTPUT_LABELREF (asm_out_file, name); + else + ASM_OUTPUT_LABELREF (asm_out_file, name); + + fputc (',', asm_out_file); + if (output_bytecode) + BC_OUTPUT_LABELREF (asm_out_file, value); + else + ASM_OUTPUT_LABELREF (asm_out_file, value); + + fputc ('\n', asm_out_file); + } + } + else if (! (what == ps_done || what == ps_start)) + warning ("malformed `#pragma weak'"); +} |