diff options
Diffstat (limited to 'gcc/java/jcf-write.c')
-rw-r--r-- | gcc/java/jcf-write.c | 1541 |
1 files changed, 1228 insertions, 313 deletions
diff --git a/gcc/java/jcf-write.c b/gcc/java/jcf-write.c index af66301..deda8c1f 100644 --- a/gcc/java/jcf-write.c +++ b/gcc/java/jcf-write.c @@ -35,19 +35,16 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ extern struct obstack temporary_obstack; -/* The buffer allocated for bytecode for the current method. */ - -struct buffer bytecode = NULL_BUFFER; - /* Make sure bytecode.data is big enough for at least N more bytes. */ #define RESERVE(N) \ - do { if (bytecode.ptr + (N) > bytecode.limit) buffer_grow (&bytecode, N); } while (0) + do { if (state->bytecode.ptr + (N) > state->bytecode.limit) \ + buffer_grow (&state->bytecode, N); } while (0) /* Add a 1-byte instruction/operand I to bytecode.data, assuming space has already been RESERVE'd. */ -#define OP1(I) (*bytecode.ptr++ = (I)) +#define OP1(I) (*state->bytecode.ptr++ = (I)) /* Like OP1, but I is a 2-byte big endian integer. */ @@ -73,12 +70,14 @@ CPool *code_cpool; /* Macro to call each time we push I words on the JVM stack. */ #define NOTE_PUSH(I) \ - do { code_SP += (I); if (code_SP > code_SP_max) code_SP_max = code_SP; } while (0) + do { state->code_SP += (I); \ + if (state->code_SP > state->code_SP_max) \ + state->code_SP_max = state->code_SP; } while (0) /* Macro to call each time we pop I words from the JVM stack. */ #define NOTE_POP(I) \ - do { code_SP -= (I); if (code_SP < 0) abort(); } while (0) + do { state->code_SP -= (I); if (state->code_SP < 0) abort(); } while (0) /* A chunk or segment of a .class file. */ @@ -94,6 +93,103 @@ struct chunk int size; }; +/* Each "block" represents a label plus the bytecode instructions following. + There may be branches out of the block, but no incoming jumps, except + to the beginning of the block. */ + +struct jcf_block +{ + /* For blocks that that are defined, the next block (in pc order). + For blocks that are the not-yet-defined end label of a LABELED_BLOCK_EXPR, + this is the next (outer) such end label, in a stack heaed by + labeled_blocks in jcf_partial. */ + struct jcf_block *next; + + /* Until perform_relocations is finished, this is the maximum possible + value of the bytecode offset at the begnning of this block. + After perform_relocations, it is the actual offset (pc). */ + int pc; + + int linenumber; + + struct chunk *chunk; + + union { + /* Set of relocations (in reverse offset order) for this block. */ + struct jcf_relocation *relocations; + + /* If this block is that of the not-yet-defined end label of + a LABELED_BLOCK_EXPR, where LABELED_BLOCK is that LABELED_BLOCK_EXPR. */ + tree labeled_block; + } u; +}; + +struct jcf_relocation +{ + /* Next relocation for the current jcf_block. */ + struct jcf_relocation *next; + + /* The (byte) offset within the current block that needs to be relocated. */ + int offset; + + /* 0 if offset is a 4-byte relative offset. + -1 if offset is a 2-byte relative offset. + < 0 if offset is the address of an instruction with a 2-byte offset + that does not have a corresponding 4-byte offset version, in which + case the absolute value of kind is the inverted opcode. + > 0 if offset is the address of an instruction (such as jsr) with a + 2-byte offset that does have a corresponding 4-byte offset version, + in which case kind is the opcode of the 4-byte version (such as jsr_w). */ + int kind; + + /* The label the relocation wants to actually transfer to. */ + struct jcf_block *label; +}; + +/* This structure is used to contain the various pieces that will + become a .class file. */ + +struct jcf_partial +{ + struct chunk *first; + struct chunk *chunk; + struct obstack *chunk_obstack; + tree current_method; + + /* List of basic blocks for the current method. */ + struct jcf_block *blocks; + struct jcf_block *last_block; + + struct localvar_info *first_lvar; + struct localvar_info *last_lvar; + int lvar_count; + + CPool cpool; + + int linenumber_count; + + /* Until perform_relocations, this is a upper bound on the number + of bytes (so far) in the instructions for the current method. */ + int code_length; + + /* Stack of undefined ending labels for LABELED_BLOCK_EXPR. */ + struct jcf_block *labeled_blocks; + + /* The current stack size (stack pointer) in the current method. */ + int code_SP; + + /* The largest extent of stack size (stack pointer) in the current method. */ + int code_SP_max; + + /* Contains a mapping from local var slot number to localvar_info. */ + struct buffer localvars; + + /* The buffer allocated for bytecode for the current jcf_block. */ + struct buffer bytecode; +}; + +static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *)); + /* Utility macros for appending (big-endian) data to a buffer. We assume a local variable 'ptr' points into where we want to write next, and we assume enoygh space has been allocated. */ @@ -104,162 +200,228 @@ struct chunk #define PUTN(P, N) (bcopy(P, ptr, N), ptr += (N)) -/* A buffer for storing line number entries for the current method. */ -struct buffer linenumbers = NULL_BUFFER; +/* Allocate a new chunk on obstack WORK, and link it in after LAST. + Set the data and size fields to DATA and SIZE, respectively. + However, if DATA is NULL and SIZE>0, allocate a buffer as well. */ + +struct chunk * +alloc_chunk (last, data, size, work) + struct chunk *last; + unsigned char *data; + int size; + struct obstack *work; +{ + struct chunk *chunk = (struct chunk *) + obstack_alloc (work, sizeof(struct chunk)); + + if (data == NULL && size > 0) + data = obstack_alloc (work, size); + + chunk->next = NULL; + chunk->data = data; + chunk->size = size; + if (last != NULL) + last->next = chunk; + return chunk; +} + +unsigned char * +append_chunk (data, size, state) + unsigned char *data; + int size; + struct jcf_partial *state; +{ + state->chunk = alloc_chunk (state->chunk, data, size, state->chunk_obstack); + if (state->first == NULL) + state->first = state->chunk; + return state->chunk->data; +} + +void +append_chunk_copy (data, size, state) + unsigned char *data; + int size; + struct jcf_partial *state; +{ + unsigned char *ptr = append_chunk (NULL, size, state); + bcopy (data, ptr, size); +} + +struct jcf_block * +gen_jcf_label (state) + struct jcf_partial *state; +{ + struct jcf_block *block = (struct jcf_block *) + obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block)); + block->next = NULL; + block->linenumber = -1; + block->pc = -1; + return block; +} + +void +finish_jcf_block (state) + struct jcf_partial *state; +{ + struct jcf_block *block = state->last_block; + struct jcf_relocation *reloc; + int pc = state->code_length; + append_chunk_copy (state->bytecode.data, BUFFER_LENGTH (&state->bytecode), + state); + BUFFER_RESET (&state->bytecode); + block->chunk = state->chunk; + + /* Calculate code_length to the maximum value it can have. */ + pc += block->chunk->size; + for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) + { + int kind = reloc->kind; + if (kind > 0) + pc += 2; /* 2-byte offset may grow to 4-byte offset */ + else if (kind < -1) + pc += 5; /* May need to add a goto_w. */ + } + state->code_length = pc; +} + +void +define_jcf_label (label, state) + struct jcf_block *label; + struct jcf_partial *state; +{ + if (state->last_block != NULL) + finish_jcf_block (state); + label->pc = state->code_length; + if (state->blocks == NULL) + state->blocks = label; + else + state->last_block->next = label; + state->last_block = label; + label->next = NULL; + label->u.relocations = NULL; +} + +struct jcf_block * +get_jcf_label_here (state) + struct jcf_partial *state; +{ + if (state->last_block != NULL && BUFFER_LENGTH (&state->bytecode) == 0) + return state->last_block; + else + { + struct jcf_block *label = gen_jcf_label (state); + define_jcf_label (label, state); + return label; + } +} -/* Append a line number entry for the given PC and LINE into - linenumbers.data. This will later before a LineNumberTable attribute. */ +/* Note a line number entry for the current PC and given LINE. */ void -put_linenumber (pc, line) - int pc, line; +put_linenumber (line, state) + int line; + struct jcf_partial *state; { - register unsigned char *ptr; - if (linenumbers.ptr == linenumbers.limit) - buffer_grow (&linenumbers, 4); - ptr = linenumbers.ptr; - PUT2 (pc); - PUT2 (line); - linenumbers.ptr = ptr; + (get_jcf_label_here (state))->linenumber = line; + state->linenumber_count++; } + /* The index of jvm local variable allocated for this DECL. - This is assign when generating .class files; - contrast DECL_LOCAL_SLOT_NUMBER whcih is set when *reading* a .class file. + This is assigned when generating .class files; + contrast DECL_LOCAL_SLOT_NUMBER which is set when *reading* a .class file. (We don't allocate DECL_LANG_SPECIFIC for locals from Java sourc code.) */ #define DECL_LOCAL_INDEX(DECL) DECL_ALIGN(DECL) struct localvar_info { - tree decl; + struct localvar_info *next; - int start_pc; - - /* Offset in LocalVariableTable. */ - int debug_offset; + tree decl; + struct jcf_block *start_label; + struct jcf_block *end_label; }; -struct buffer localvars = NULL_BUFFER; - -#define localvar_buffer ((struct localvar_info*) localvars.data) -#define localvar_max ((struct localvar_info*) localvars.ptr - localvar_buffer) - -/* A buffer for storing LocalVariableTable entries entries. */ - -struct buffer localvartable = NULL_BUFFER; +#define localvar_buffer ((struct localvar_info**) state->localvars.data) +#define localvar_max \ + ((struct localvar_info**) state->localvars.ptr - localvar_buffer) int -localvar_alloc (decl, start_pc) +localvar_alloc (decl, state) tree decl; - int start_pc; + struct jcf_partial *state; { + struct jcf_block *start_label = get_jcf_label_here (state); int wide = TYPE_IS_WIDE (TREE_TYPE (decl)); int index; - register struct localvar_info *info = (struct localvar_info*)localvars.data; - register struct localvar_info *limit = (struct localvar_info*)localvars.ptr; - for (index = 0; info < limit; index++, info++) + register struct localvar_info *info; + register struct localvar_info **ptr = localvar_buffer; + register struct localvar_info **limit + = (struct localvar_info**) state->localvars.ptr; + for (index = 0; ptr < limit; index++, ptr++) { - if (info->decl == NULL_TREE - && (! wide || (info+1)->decl == NULL_TREE)) + if (ptr[0] == NULL + && (! wide || ((ptr+1) < limit && ptr[1] == NULL))) break; } - if (info == limit) + if (ptr == limit) { - buffer_grow (&localvars, sizeof (struct localvar_info)); - info = (struct localvar_info*)localvars.data + index; - localvars.ptr = (unsigned char *) (info + 1 + wide); + buffer_grow (&state->localvars, 2 * sizeof (struct localvar_info*)); + ptr = (struct localvar_info**) state->localvars.data + index; + state->localvars.ptr = (unsigned char *) (ptr + 1 + wide); } - info->decl = decl; + info = (struct localvar_info *) + obstack_alloc (state->chunk_obstack, sizeof (struct localvar_info)); + ptr[0] = info; if (wide) - (info+1)->decl = TYPE_SECOND; + ptr[1] = (struct localvar_info *)(~0); DECL_LOCAL_INDEX (decl) = index; - info->start_pc = start_pc; + info->decl = decl; + info->start_label = start_label; if (DECL_NAME (decl) != NULL_TREE) { /* Generate debugging info. */ - int i; - register unsigned char *ptr; - buffer_grow (&localvartable, 10); - ptr = localvartable.ptr; - info->debug_offset = ptr - localvartable.data; - PUT2 (start_pc); - PUT2 (0); /* length - fill in later */ - i = find_utf8_constant (code_cpool, DECL_NAME (decl)); - PUT2 (i); /* name_index*/ - i = find_utf8_constant (code_cpool, - build_java_signature (TREE_TYPE (decl))); - PUT2 (i); /* descriptor_index */ - PUT2 (index); - localvartable.ptr = ptr; + info->next = NULL; + if (state->last_lvar != NULL) + state->last_lvar->next = info; + else + state->first_lvar = info; + state->last_lvar = info; + state->lvar_count++; } - else - info->debug_offset = -1; } int -localvar_free (decl, end_pc) - tree decl; - int end_pc; +localvar_free (decl, state) + tree decl; + struct jcf_partial *state; { - register unsigned char *ptr; + struct jcf_block *end_label = get_jcf_label_here (state); int index = DECL_LOCAL_INDEX (decl); - register struct localvar_info *info = &localvar_buffer [index]; + register struct localvar_info **ptr = &localvar_buffer [index]; + register struct localvar_info *info = *ptr; int wide = TYPE_IS_WIDE (TREE_TYPE (decl)); int i; - i = info->debug_offset; - if (i >= 0) - { - register unsigned char *ptr; - /* Point to length field of local_variable_table. */ - ptr = localvartable.data + i + 2; - i = end_pc - info->start_pc; - PUT2 (i); - } + info->end_label = end_label; if (info->decl != decl) abort (); - info->decl = NULL_TREE; + ptr[0] = NULL; if (wide) { - info++; - if (info->decl != TYPE_SECOND) + if (ptr[1] != (struct localvar_info *)(~0)) abort (); - info->decl = NULL_TREE; + ptr[1] = NULL; } - } #define STACK_TARGET 1 #define IGNORE_TARGET 2 -/* Allocate a new chunk on obstack WORK, and link it in after LAST. - Set the data and size fields to DATA and SIZE, respectively. - However, if DATA is NULL and SIZE>0, allocate a buffer as well. */ - -struct chunk * -alloc_chunk (last, data, size, work) - struct chunk *last; - unsigned char *data; - int size; - struct obstack *work; -{ - struct chunk *chunk = (struct chunk *) - obstack_alloc (work, sizeof(struct chunk)); - - if (data == NULL && size > 0) - data = obstack_alloc (work, size); - - chunk->next = NULL; - chunk->data = data; - chunk->size = size; - last->next = chunk; - return chunk; -} - /* Get the access flags of a class (TYPE_DECL), a method (FUNCTION_DECL), or a field (FIELD_DECL or VAR_DECL, if static), as encoded in a .class file. */ @@ -327,9 +489,10 @@ write_chunks (stream, chunks) fwrite (chunks->data, chunks->size, 1, stream); } -void -push_constant1 (index) +static void +push_constant1 (index, state) int index; + struct jcf_partial *state; { if (index < 256) { @@ -343,18 +506,23 @@ push_constant1 (index) } } -void -push_constant2 (index) +static void +push_constant2 (index, state) int index; + struct jcf_partial *state; { RESERVE (3); OP1 (OPCODE_ldc2_w); OP2 (index); } -void -push_int_const (i) +/* Push 32-bit integer constant on VM stack. + Caller is responsible for doing NOTE_PUSH. */ + +static void +push_int_const (i, state) HOST_WIDE_INT i; + struct jcf_partial *state; { RESERVE(3); if (i >= -1 && i <= 5) @@ -368,17 +536,22 @@ push_int_const (i) { OP1(OPCODE_sipush); OP2(i); + NOTE_PUSH (1); } else { - i = find_constant1 (code_cpool, CONSTANT_Integer, i & 0xFFFFFFFF); + i = find_constant1 (&state->cpool, CONSTANT_Integer, i & 0xFFFFFFFF); push_constant1 (i); } } -void -push_long_const (lo, hi) +/* Push 64-bit long constant on VM stack. + Caller is responsible for doing NOTE_PUSH. */ + +static void +push_long_const (lo, hi, state) HOST_WIDE_INT lo, hi; + struct jcf_partial *state; { if (hi == 0 && lo >= 0 && lo <= 1) { @@ -388,7 +561,7 @@ push_long_const (lo, hi) #if 0 else if ((jlong) (jint) i == i) { - push_int_const ((jint) i); + push_int_const ((jint) i, state); RESERVE (1); OP1 (OPCODE_i2l); } @@ -397,18 +570,19 @@ push_long_const (lo, hi) { HOST_WIDE_INT w1, w2; lshift_double (lo, hi, -32, 64, &w1, &w2, 1); - hi = find_constant1 (code_cpool, CONSTANT_Long, + hi = find_constant1 (&state->cpool, CONSTANT_Long, w1 & 0xFFFFFFFF, lo & 0xFFFFFFFF); push_constant2 (hi); } } -void -field_op (field, opcode) +static void +field_op (field, opcode, state) tree field; int opcode; + struct jcf_partial *state; { - int index = find_fieldref_index (code_cpool, field); + int index = find_fieldref_index (&state->cpool, field); RESERVE (3); OP1 (opcode); OP2 (index); @@ -424,10 +598,12 @@ adjust_typed_op (type) { switch (TREE_CODE (type)) { - case BOOLEAN_TYPE: return 5; - case CHAR_TYPE: return 6; case POINTER_TYPE: case RECORD_TYPE: return 4; + case BOOLEAN_TYPE: + return TYPE_PRECISION (type) == 32 ? 0 : 5; + case CHAR_TYPE: + return TYPE_PRECISION (type) == 32 ? 0 : 6; case INTEGER_TYPE: switch (TYPE_PRECISION (type)) { @@ -448,14 +624,15 @@ adjust_typed_op (type) abort (); } -void -maybe_wide (opcode, index) +static void +maybe_wide (opcode, index, state) int opcode, index; + struct jcf_partial *state; { if (index >= 256) { RESERVE (4); - OP1 (196); /* wide */ + OP1 (OPCODE_wide); OP1 (opcode); OP2 (index); } @@ -467,21 +644,382 @@ maybe_wide (opcode, index) } } -#define PC BUFFER_LENGTH(&bytecode) +/* Compile code to duplicate with offset, where + SIZE is the size of the stack item to duplicate (1 or 2), abd + OFFSET is where to insert the result (must be 0, 1, or 2). + (The new words get inserted at stack[SP-size-offset].) */ -/* Generate byetcode for sub-expression EXP of METHOD. - TARGET is one of STACK_TARGET or IGNORE_TARGET. */ +static void +emit_dup (size, offset, state) + int size, offset; + struct jcf_partial *state; +{ + int kind; + if (size == 0) + return; + RESERVE(1); + if (offset == 0) + kind = size == 1 ? OPCODE_dup : OPCODE_dup2; + else if (offset == 1) + kind = size == 1 ? OPCODE_dup_x1 : OPCODE_dup2_x1; + else if (offset == 2) + kind = size == 1 ? OPCODE_dup_x2 : OPCODE_dup2_x2; + else + abort(); + OP1 (kind); + NOTE_PUSH (size); +} + +static void +emit_pop (size, state) + int size; + struct jcf_partial *state; +{ + RESERVE (1); + OP1 (OPCODE_pop - 1 + size); +} + +static void +emit_iinc (var, value, state) + tree var; + int value; + struct jcf_partial *state; +{ + int slot = DECL_LOCAL_INDEX (var); + + if (value < -128 || value > 127 || slot >= 256) + { + RESERVE (6); + OP1 (OPCODE_wide); + OP1 (OPCODE_iinc); + OP2 (slot); + OP2 (value); + } + else + { + RESERVE (3); + OP1 (OPCODE_iinc); + OP1 (slot); + OP1 (value); + } +} + +static void +emit_load_or_store (var, opcode, state) + tree var; + struct jcf_partial *state; +{ + tree type = TREE_TYPE (var); + int kind = adjust_typed_op (type); + int index = DECL_LOCAL_INDEX (var); + if (index <= 3) + { + RESERVE (1); + OP1 (opcode + 5 + 4 * kind + index); /* [ilfda]{load,store}_[0123] */ + } + else + maybe_wide (opcode + kind, index); /* [ilfda]{load,store} */ +} + +static void +emit_load (var, state) + tree var; + struct jcf_partial *state; +{ + emit_load_or_store (var, OPCODE_iload, state); + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (var)) ? 2 : 1); +} + +static void +emit_store (var, state) + tree var; + struct jcf_partial *state; +{ + emit_load_or_store (var, OPCODE_istore, state); + NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (var)) ? 2 : 1); +} + +static void +emit_binop (opcode, type, state) + enum java_opcode opcode; + tree type; + struct jcf_partial *state; +{ + int size = TYPE_IS_WIDE (type) ? 2 : 1; + RESERVE(1); + OP1 (opcode); + NOTE_POP (size); +} + +/* Emit a conditional jump to TARGET with a 2-byte relative jump offset + The opcode is OPCODE, the inverted opcode is INV_OPCODE. */ + +static void +emit_if (target, opcode, inv_opcode, state) + struct jcf_block *target; + int opcode, inv_opcode; + struct jcf_partial *state; +{ + struct jcf_relocation *reloc = (struct jcf_relocation *) + obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation)); + struct jcf_block *block = state->last_block; + reloc->next = block->u.relocations; + block->u.relocations = reloc; + OP1 (opcode); + reloc->offset = BUFFER_LENGTH (&state->bytecode); + OP2 (1); // 1 byte from reloc back to start of instruction. + reloc->kind = - inv_opcode; + reloc->label = target; +} + +static void +emit_goto_or_jsr (target, opcode, opcode_w, state) + struct jcf_block *target; + int opcode, opcode_w; + struct jcf_partial *state; +{ + struct jcf_relocation *reloc = (struct jcf_relocation *) + obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation)); + struct jcf_block *block = state->last_block; + reloc->next = block->u.relocations; + block->u.relocations = reloc; + OP1 (opcode); + reloc->offset = BUFFER_LENGTH (&state->bytecode); + OP2 (1); // 1 byte from reloc back to start of instruction. + reloc->kind = opcode_w; + reloc->label = target; +} + +static void +emit_goto (target, state) + struct jcf_block *target; + struct jcf_partial *state; +{ + emit_goto_or_jsr (target, OPCODE_goto, OPCODE_goto_w, state); +} + +static void +emit_jsr (target, state) + struct jcf_block *target; + struct jcf_partial *state; +{ + emit_goto_or_jsr (target, OPCODE_jsr, OPCODE_jsr_w, state); +} + +/* Generate code to evaluate EXP. If the result is true, + branch to TRUE_LABEL; otherwise, branch to FALSE_LABEL. + TRUE_BRANCH_FIRST is a code geneation hint that the + TRUE_LABEL may follow right after this. (The idea is that we + may be able to optimize away GOTO TRUE_LABEL; TRUE_LABEL:) */ void -generate_bytecode_insns (method, exp, target) - tree method; +generate_bytecode_conditional (exp, true_label, false_label, + true_branch_first, state) + tree exp; + struct jcf_block *true_label; + struct jcf_block *false_label; + int true_branch_first; + struct jcf_partial *state; +{ + int kind; + tree exp0, exp1, type; + int save_SP = state->code_SP; + enum java_opcode op, negop; + switch (TREE_CODE (exp)) + { + case INTEGER_CST: + emit_goto (integer_zerop (exp) ? false_label : true_label, state); + break; + case COND_EXPR: + { + struct jcf_block *then_label = gen_jcf_label (state); + struct jcf_block *else_label = gen_jcf_label (state); + int save_SP_before, save_SP_after; + generate_bytecode_conditional (TREE_OPERAND (exp, 0), + then_label, else_label, 1, state); + define_jcf_label (then_label, state); + save_SP_before = state->code_SP; + generate_bytecode_conditional (TREE_OPERAND (exp, 1), + true_label, false_label, 1, state); + save_SP_after = state->code_SP; + state->code_SP = save_SP_before; + define_jcf_label (else_label, state); + generate_bytecode_conditional (TREE_OPERAND (exp, 2), + true_label, false_label, + true_branch_first, state); + if (state->code_SP != save_SP_after) + fatal ("internal error non-matching SP"); + } + break; + case TRUTH_ANDIF_EXPR: + { + struct jcf_block *next_label = gen_jcf_label (state); + generate_bytecode_conditional (TREE_OPERAND (exp, 0), + next_label, false_label, 1, state); + define_jcf_label (next_label, state); + generate_bytecode_conditional (TREE_OPERAND (exp, 1), + true_label, false_label, 1, state); + } + break; + case TRUTH_ORIF_EXPR: + { + struct jcf_block *next_label = gen_jcf_label (state); + generate_bytecode_conditional (TREE_OPERAND (exp, 0), + true_label, next_label, 1, state); + define_jcf_label (next_label, state); + generate_bytecode_conditional (TREE_OPERAND (exp, 1), + true_label, false_label, 1, state); + } + break; + compare_1: + /* Assuming op is one of the 2-operand if_icmp<COND> instructions, + set it to the corresponding 1-operand if<COND> instructions. */ + op = op - 6; + /* FALLTHROUGH */ + compare_2: + /* The opcodes with their inverses are allocated in pairs. + E.g. The inverse of if_icmplt (161) is if_icmpge (162). */ + negop = (op & 1) ? op + 1 : op - 1; + compare_2_ptr: + if (true_branch_first) + { + emit_if (false_label, negop, op, state); + emit_goto (true_label, state); + } + else + { + emit_if (true_label, op, negop, state); + emit_goto (false_label, state); + } + break; + case EQ_EXPR: + op = OPCODE_if_icmpeq; + goto compare; + case NE_EXPR: + op = OPCODE_if_icmpne; + goto compare; + case GT_EXPR: + op = OPCODE_if_icmpgt; + goto compare; + case LT_EXPR: + op = OPCODE_if_icmplt; + goto compare; + case GE_EXPR: + op = OPCODE_if_icmpge; + goto compare; + case LE_EXPR: + op = OPCODE_if_icmple; + goto compare; + compare: + exp0 = TREE_OPERAND (exp, 0); + exp1 = TREE_OPERAND (exp, 1); + type = TREE_TYPE (exp0); + switch (TREE_CODE (type)) + { + case POINTER_TYPE: case RECORD_TYPE: + switch (TREE_CODE (exp)) + { + case EQ_EXPR: op = OPCODE_if_acmpeq; break; + case NE_EXPR: op = OPCODE_if_acmpne; break; + default: abort(); + } + if (integer_zerop (exp1) || integer_zerop (exp0)) + { + generate_bytecode_insns (integer_zerop (exp1) ? exp0 : exp0, + STACK_TARGET, state); + op = op + (OPCODE_ifnull - OPCODE_if_acmpeq); + negop = (op & 1) ? op - 1 : op + 1; + NOTE_POP (1); + goto compare_2_ptr; + } + generate_bytecode_insns (exp0, STACK_TARGET, state); + generate_bytecode_insns (exp1, STACK_TARGET, state); + NOTE_POP (2); + goto compare_2; + case REAL_TYPE: + fatal ("float comparison not implemented"); + case INTEGER_TYPE: + if (TYPE_PRECISION (type) > 32) + { + generate_bytecode_insns (exp0, STACK_TARGET, state); + generate_bytecode_insns (exp1, STACK_TARGET, state); + NOTE_POP (4); + RESERVE (1); + OP1 (OPCODE_lcmp); + goto compare_1; + } + /* FALLTHOUGH */ + default: + if (integer_zerop (exp1)) + { + generate_bytecode_insns (exp0, STACK_TARGET, state); + NOTE_POP (1); + goto compare_1; + } + if (integer_zerop (exp0)) + { + switch (op) + { + case OPCODE_if_icmplt: + case OPCODE_if_icmpge: + op += 2; + break; + case OPCODE_if_icmpgt: + case OPCODE_if_icmple: + op -= 2; + break; + } + generate_bytecode_insns (exp1, STACK_TARGET, state); + NOTE_POP (1); + goto compare_1; + } + generate_bytecode_insns (exp0, STACK_TARGET, state); + generate_bytecode_insns (exp1, STACK_TARGET, state); + NOTE_POP (2); + goto compare_2; + } + + default: + generate_bytecode_insns (exp, STACK_TARGET, state); + NOTE_POP (1); + if (true_branch_first) + { + emit_if (false_label, OPCODE_ifeq, OPCODE_ifne, state); + emit_goto (true_label, state); + } + else + { + emit_if (true_label, OPCODE_ifne, OPCODE_ifeq, state); + emit_goto (false_label, state); + } + break; + } + if (save_SP != state->code_SP) + fatal ("inetrnal error - SP mismatch"); +} + +/* Generate bytecode for sub-expression EXP of METHOD. + TARGET is one of STACK_TARGET or IGNORE_TARGET. */ + +static void +generate_bytecode_insns (exp, target, state) tree exp; int target; + struct jcf_partial *state; { - rtx value; - tree type = TREE_TYPE (exp); + tree type; enum java_opcode jopcode; int op; + HOST_WIDE_INT value; + int post_op; + int size; + int offset; + + if (exp == NULL && target == IGNORE_TARGET) + return; + + type = TREE_TYPE (exp); + switch (TREE_CODE (exp)) { case BLOCK: @@ -491,21 +1029,21 @@ generate_bytecode_insns (method, exp, target) for (local = BLOCK_EXPR_DECLS (exp); local; ) { tree next = TREE_CHAIN (local); - localvar_alloc (local, PC); + localvar_alloc (local, state); local = next; } - generate_bytecode_insns (method, BLOCK_EXPR_BODY (exp), target); + generate_bytecode_insns (BLOCK_EXPR_BODY (exp), target, state); for (local = BLOCK_EXPR_DECLS (exp); local; ) { tree next = TREE_CHAIN (local); - localvar_free (local, PC); + localvar_free (local, state); local = next; } } break; case COMPOUND_EXPR: - generate_bytecode_insns (method, TREE_OPERAND (exp, 0), IGNORE_TARGET); - generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target); + generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state); + generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); break; case EXPR_WITH_FILE_LOCATION: { @@ -514,8 +1052,8 @@ generate_bytecode_insns (method, exp, target) input_filename = EXPR_WFL_FILENAME (exp); lineno = EXPR_WFL_LINENO (exp); if (EXPR_WFL_EMIT_LINE_NOTE (exp)) - put_linenumber (PC, EXPR_WFL_LINENO (exp)); - generate_bytecode_insns (method, EXPR_WFL_NODE (exp), target); + put_linenumber (EXPR_WFL_LINENO (exp), state); + generate_bytecode_insns (EXPR_WFL_NODE (exp), target, state); input_filename = saved_input_filename; lineno = saved_lineno; } @@ -532,46 +1070,39 @@ generate_bytecode_insns (method, exp, target) } else if (TYPE_PRECISION (type) <= 32) { - push_int_const (TREE_INT_CST_LOW (exp)); + push_int_const (TREE_INT_CST_LOW (exp), state); NOTE_PUSH (1); } else { - push_long_const (TREE_INT_CST_LOW (exp), TREE_INT_CST_HIGH (exp)); + push_long_const (TREE_INT_CST_LOW (exp), TREE_INT_CST_HIGH (exp), + state); NOTE_PUSH (2); } break; case VAR_DECL: if (TREE_STATIC (exp)) { - field_op (exp, OPCODE_getstatic); + field_op (exp, OPCODE_getstatic, state); + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); break; } /* ... fall through ... */ case PARM_DECL: - { - int kind = adjust_typed_op (type); - int index = DECL_LOCAL_INDEX (exp); - if (index <= 3) - { - RESERVE (1); - OP1 (26 + 4 * kind + index); /* [ilfda]load_[0123] */ - } - else - maybe_wide (21 + kind, index); /* [ilfda]load */ - } + emit_load (exp, state); break; case INDIRECT_REF: - generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target); + generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); break; case ARRAY_REF: - generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target); - generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target); + generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); + generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); if (target != IGNORE_TARGET) { jopcode = OPCODE_iaload + adjust_typed_op (type); RESERVE(1); OP1 (jopcode); + NOTE_POP (2); } break; case COMPONENT_REF: @@ -579,8 +1110,8 @@ generate_bytecode_insns (method, exp, target) tree obj = TREE_OPERAND (exp, 0); tree field = TREE_OPERAND (exp, 1); int is_static = FIELD_STATIC (field); - generate_bytecode_insns (method, obj, - is_static ? IGNORE_TARGET : target); + generate_bytecode_insns (obj, + is_static ? IGNORE_TARGET : target, state); if (target != IGNORE_TARGET) { if (DECL_NAME (field) == length_identifier_node && !is_static @@ -590,10 +1121,54 @@ generate_bytecode_insns (method, exp, target) OP1 (OPCODE_arraylength); } else - field_op (field, is_static ? OPCODE_getstatic : OPCODE_getfield); + { + field_op (field, is_static ? OPCODE_getstatic : OPCODE_getfield, + state); + if (! is_static) + NOTE_POP (1); + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1); + } } } break; + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case EQ_EXPR: + case NE_EXPR: + case GT_EXPR: + case LT_EXPR: + case GE_EXPR: + case LE_EXPR: + { + struct jcf_block *then_label = gen_jcf_label (state); + struct jcf_block *else_label = gen_jcf_label (state); + struct jcf_block *end_label = gen_jcf_label (state); + generate_bytecode_conditional (exp, + then_label, else_label, 1, state); + define_jcf_label (then_label, state); + push_int_const (1, state); + emit_goto (end_label, state); + define_jcf_label (else_label, state); + push_int_const (0, state); + define_jcf_label (end_label, state); + NOTE_PUSH (1); + } + break; + case COND_EXPR: + { + struct jcf_block *then_label = gen_jcf_label (state); + struct jcf_block *else_label = gen_jcf_label (state); + struct jcf_block *end_label = gen_jcf_label (state); + generate_bytecode_conditional (TREE_OPERAND (exp, 0), + then_label, else_label, 1, state); + define_jcf_label (then_label, state); + generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); + emit_goto (end_label, state); + define_jcf_label (else_label, state); + generate_bytecode_insns (TREE_OPERAND (exp, 2), target, state); + define_jcf_label (end_label, state); + } + break; case RETURN_EXPR: if (!TREE_OPERAND (exp, 0)) op = OPCODE_return; @@ -604,89 +1179,234 @@ generate_bytecode_insns (method, exp, target) abort (); exp = TREE_OPERAND (exp, 1); op = OPCODE_ireturn + adjust_typed_op (TREE_TYPE (exp)); - generate_bytecode_insns (method, exp, STACK_TARGET); + generate_bytecode_insns (exp, STACK_TARGET, state); } RESERVE (1); OP1 (op); break; - case MODIFY_EXPR: + case LABELED_BLOCK_EXPR: { - tree lhs = TREE_OPERAND (exp, 0); - tree rhs = TREE_OPERAND (exp, 1); - HOST_WIDE_INT value; + struct jcf_block *end_label = gen_jcf_label (state); + end_label->next = state->labeled_blocks; + state->labeled_blocks = end_label; + end_label->u.labeled_block = exp; + if (LABELED_BLOCK_BODY (exp)) + generate_bytecode_insns (LABELED_BLOCK_BODY (exp), target, state); + if (state->labeled_blocks != end_label) + abort(); + state->labeled_blocks = end_label->next; + define_jcf_label (end_label, state); + } + break; + case LOOP_EXPR: + { + tree body = TREE_OPERAND (exp, 0); #if 0 - if (TREE_CODE (rhs) == PLUS_EXPR - && TREE_CODE (lhs) == VAR_DECL - /* && FIXME lhs is a local variable */ - && TYPE_MODE (TREE)TYPE (lhs) == SImode /* ??? */ - && TREE_OPERAND (rhs, 0) == lhs - && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST - /* or vice versa FIXME */ - && (value = TREE_INT_CST_LOW (TREE_OPERAND (rhs, 1)), - (value >= -32768 && value <= 32767))) + if (TREE_CODE (body) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (body, 0)) == EXIT_EXPR) { - emit_insn (gen_rtx (SET, SImode, - DECL_RTL (lhs), - gen_rtx (PLUS, SImode, - DECL_RTL (lhs), - gen_rtx_CONST_INT (SImode, value)))); - return DECL_RTL (lhs); + /* Optimize: H: if (TEST) GOTO L; BODY; GOTO H; L: + to: GOTO L; BODY; L: if (!TEST) GOTO L; */ + struct jcf_block *head_label; + struct jcf_block *body_label; + struct jcf_block *end_label = gen_jcf_label (state); + struct jcf_block *exit_label = state->labeled_blocks; + head_label = gen_jcf_label (state); + emit_goto (head_label, state); + body_label = get_jcf_label_here (state); + generate_bytecode_insns (TREE_OPERAND (body, 1), target, state); + define_jcf_label (head_label, state); + generate_bytecode_conditional (TREE_OPERAND (body, 0), + end_label, body_label, 1, state); + define_jcf_label (end_label, state); } + else #endif - if (TREE_CODE (lhs) == COMPONENT_REF) - generate_bytecode_insns (method, TREE_OPERAND (lhs, 0), STACK_TARGET); - else if (TREE_CODE (lhs) == ARRAY_REF) - { - generate_bytecode_insns (method, - TREE_OPERAND (lhs, 0), STACK_TARGET); - generate_bytecode_insns (method, - TREE_OPERAND (lhs, 1), STACK_TARGET); - } - generate_bytecode_insns (method, rhs, STACK_TARGET); - if (target != IGNORE_TARGET) - { - RESERVE (1); - OP1 (TYPE_IS_WIDE (type) ? OPCODE_dup2_x1 : OPCODE_dup_x1); - } - if (TREE_CODE (lhs) == COMPONENT_REF) { - tree field = TREE_OPERAND (lhs, 1); - field_op (field, - FIELD_STATIC (field) ? OPCODE_putstatic - : OPCODE_putfield); + struct jcf_block *head_label = get_jcf_label_here (state); + generate_bytecode_insns (body, IGNORE_TARGET, state); + emit_goto (head_label, state); } - else if (TREE_CODE (lhs) == VAR_DECL - || TREE_CODE (lhs) == PARM_DECL) + } + break; + case EXIT_EXPR: + { + struct jcf_block *label = state->labeled_blocks; + struct jcf_block *end_label = gen_jcf_label (state); + generate_bytecode_conditional (TREE_OPERAND (exp, 0), + label, end_label, 0, state); + define_jcf_label (end_label, state); + } + break; + case EXIT_BLOCK_EXPR: + { + struct jcf_block *label = state->labeled_blocks; + if (TREE_OPERAND (exp, 1) != NULL) goto notimpl; + while (label->u.labeled_block != TREE_OPERAND (exp, 0)) + label = label->next; + emit_goto (label, state); + } + break; + + case PREDECREMENT_EXPR: value = -1; post_op = 0; goto increment; + case PREINCREMENT_EXPR: value = 1; post_op = 0; goto increment; + case POSTDECREMENT_EXPR: value = -1; post_op = 1; goto increment; + case POSTINCREMENT_EXPR: value = 1; post_op = 1; goto increment; + increment: + + exp = TREE_OPERAND (exp, 0); + type = TREE_TYPE (exp); + size = TYPE_IS_WIDE (type) ? 2 : 1; + if ((TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL) + && ! TREE_STATIC (exp) + && TREE_CODE (type) == INTEGER_TYPE + && TYPE_PRECISION (type) == 32) + { + if (target != IGNORE_TARGET && post_op) + emit_load (exp, state); + emit_iinc (exp, value, state); + if (target != IGNORE_TARGET) + { + if (! post_op) + emit_load (exp, state); + NOTE_PUSH (1); + } + break; + } + if (TREE_CODE (exp) == COMPONENT_REF) + { + generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); + emit_dup (1, 0, state); + /* Stack: ..., objectref, objectref. */ + field_op (TREE_OPERAND (exp, 1), OPCODE_getstatic, state); + NOTE_PUSH (size); + /* Stack: ..., objectref, oldvalue. */ + offset = 1; + } + else if (TREE_CODE (exp) == ARRAY_REF) + { + generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); + generate_bytecode_insns (TREE_OPERAND (exp, 1), STACK_TARGET, state); + emit_dup (2, 0, state); + /* Stack: ..., array, index, array, index. */ + jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (exp)); + RESERVE(1); + OP1 (jopcode); + NOTE_POP (2-size); + /* Stack: ..., array, index, oldvalue. */ + offset = 2; + } + else if (TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL) + { + generate_bytecode_insns (exp, STACK_TARGET, state); + /* Stack: ..., oldvalue. */ + offset = 0; + } + else + abort (); + + if (target != IGNORE_TARGET && post_op) + emit_dup (size, offset, state); + /* Stack, if ARRAY_REF: ..., [result, ] array, index, oldvalue. */ + /* Stack, if COMPONENT_REF: ..., [result, ] objectref, oldvalue. */ + /* Stack, otherwise: ..., [result, ] oldvalue. */ + push_int_const (value, state); /* FIXME - assumes int! */ + NOTE_PUSH (1); + emit_binop (OPCODE_iadd + adjust_typed_op (type), type, state); + if (target != IGNORE_TARGET && ! post_op) + emit_dup (size, offset, state); + /* Stack: ..., [result,] newvalue. */ + goto finish_assignment; + + case MODIFY_EXPR: + { + tree lhs = TREE_OPERAND (exp, 0); + tree rhs = TREE_OPERAND (exp, 1); + + /* See if we can use the iinc instruction. */ + if ((TREE_CODE (lhs) == VAR_DECL || TREE_CODE (lhs) == PARM_DECL) + && ! TREE_STATIC (lhs) + && TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE + && TYPE_PRECISION (TREE_TYPE (lhs)) == 32 + && (TREE_CODE (rhs) == PLUS_EXPR || TREE_CODE (rhs) == MINUS_EXPR)) { - if (FIELD_STATIC (lhs)) + tree arg0 = TREE_OPERAND (rhs, 0); + tree arg1 = TREE_OPERAND (rhs, 1); + HOST_WIDE_INT min_value = -32768; + HOST_WIDE_INT max_value = 32767; + if (TREE_CODE (rhs) == MINUS_EXPR) { - field_op (lhs, OPCODE_putstatic); + min_value++; + max_value++; } - else + else if (arg1 == lhs) { - int index = DECL_LOCAL_INDEX (lhs); - int opcode = adjust_typed_op (TREE_TYPE (lhs)); - if (index <= 3) - { - RESERVE (1); - opcode = 59 + 4 * opcode + index; - OP1 (opcode); /* [ilfda]store_[0123] */ - } - else + arg0 = arg1; + arg1 = TREE_OPERAND (rhs, 0); + } + if (lhs == arg0 && TREE_CODE (arg1) == INTEGER_CST) + { + HOST_WIDE_INT hi_value = TREE_INT_CST_HIGH (arg1); + value = TREE_INT_CST_LOW (arg1); + if ((hi_value == 0 && value <= max_value) + || (hi_value == -1 && value >= min_value)) { - maybe_wide (54 + opcode, index); /* [ilfda]store */ + if (TREE_CODE (rhs) == MINUS_EXPR) + value = -value; + emit_iinc (lhs, value, state); + break; } } } + + if (TREE_CODE (lhs) == COMPONENT_REF) + generate_bytecode_insns (TREE_OPERAND (lhs, 0), STACK_TARGET, state); else if (TREE_CODE (lhs) == ARRAY_REF) { - jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (lhs)); - RESERVE(1); - OP1 (jopcode); + generate_bytecode_insns (TREE_OPERAND(lhs, 0), STACK_TARGET, state); + generate_bytecode_insns (TREE_OPERAND(lhs, 1), STACK_TARGET, state); } - else - fatal ("internal error (bad lhs to MODIFY_EXPR)"); + generate_bytecode_insns (rhs, STACK_TARGET, state); + if (target != IGNORE_TARGET) + emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , 1, state); + exp = lhs; } + /* FALLTHOUGH */ + + finish_assignment: + if (TREE_CODE (exp) == COMPONENT_REF) + { + tree field = TREE_OPERAND (exp, 1); + if (! FIELD_STATIC (field)) + NOTE_POP (1); + field_op (field, + FIELD_STATIC (field) ? OPCODE_putstatic + : OPCODE_putfield, + state); + + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1); + } + else if (TREE_CODE (exp) == VAR_DECL + || TREE_CODE (exp) == PARM_DECL) + { + if (FIELD_STATIC (exp)) + { + field_op (exp, OPCODE_putstatic, state); + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); + } + else + emit_store (exp, state); + } + else if (TREE_CODE (exp) == ARRAY_REF) + { + NOTE_POP (2); + jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp)); + RESERVE(1); + OP1 (jopcode); + NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); + } + else + fatal ("internal error (bad lhs to MODIFY_EXPR)"); break; case PLUS_EXPR: jopcode = OPCODE_iadd + adjust_typed_op (type); @@ -702,25 +1422,24 @@ generate_bytecode_insns (method, exp, target) jopcode = OPCODE_idiv + adjust_typed_op (type); goto binop; binop: - generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target); - generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target); + generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); + generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); if (target == STACK_TARGET) - { - RESERVE(1); - OP1 (jopcode); - } + emit_binop (jopcode, type, state); break; case CALL_EXPR: { tree t; + int save_SP = state->code_SP; for (t = TREE_OPERAND (exp, 1); t != NULL_TREE; t = TREE_CHAIN (t)) { - generate_bytecode_insns (method, TREE_VALUE (t), STACK_TARGET); + generate_bytecode_insns (TREE_VALUE (t), STACK_TARGET, state); } t = TREE_OPERAND (exp, 0); + state->code_SP = save_SP; if (TREE_CODE (t) == FUNCTION_DECL) { - int index = find_methodref_index (code_cpool, t); + int index = find_methodref_index (&state->cpool, t); RESERVE (3); if (DECL_CONSTRUCTOR_P (t)) OP1 (OPCODE_invokespecial); @@ -729,59 +1448,232 @@ generate_bytecode_insns (method, exp, target) else OP1 (OPCODE_invokevirtual); OP2 (index); + t = TREE_TYPE (TREE_TYPE (t)); + if (TREE_CODE (t) != VOID_TYPE) + { + int size = TYPE_IS_WIDE (t) ? 2 : 1; + if (target == IGNORE_TARGET) + emit_pop (size, state); + else + NOTE_PUSH (size); + } break; } } /* fall through */ + notimpl: default: - error("internal error - tree code not implemented: ", TREE_CODE (exp)); + error("internal error - tree code not implemented: %s", + tree_code_name [(int) TREE_CODE (exp)]); } } +void +perform_relocations (state) + struct jcf_partial *state; +{ + struct jcf_block *block; + struct jcf_relocation *reloc; + int pc; + int shrink; + + /* Figure out the actual locations of each block. */ + pc = 0; + shrink = 0; + for (block = state->blocks; block != NULL; block = block->next) + { + int block_size = block->chunk->size; + + block->pc = pc; + + /* Optimize GOTO L; L: by getting rid of the redundant goto. + Assumes relocations are in reverse order. */ + reloc = block->u.relocations; + while (reloc != NULL + && reloc->label->pc == block->next->pc + && reloc->offset + 2 == block_size + && reloc->kind == OPCODE_goto_w) + { + reloc = reloc->next; + block->u.relocations = reloc; + block->chunk->size -= 3; + block_size -= 3; + shrink += 3; + } + + for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) + { + if (reloc->kind < -1 || reloc->kind > 0) + { + int delta = reloc->label->pc - (pc + reloc->offset - 1); + int expand = reloc->kind > 0 ? 2 : 5; + + if (delta > 0) + delta -= shrink; + if (delta >= -32768 && delta <= 32767) + { + shrink += expand; + reloc->kind = -1; + } + else + block_size += expand; + } + } + pc += block_size; + } + + for (block = state->blocks; block != NULL; block = block->next) + { + struct chunk *chunk = block->chunk; + int old_size = chunk->size; + int next_pc = block->next == NULL ? pc : block->next->pc; + int new_size = next_pc - block->pc; + int offset = 0; + unsigned char *new_ptr; + unsigned char *old_buffer = chunk->data; + unsigned char *old_ptr = old_buffer + old_size; + int new_end = new_size; + if (new_size != old_size) + { + chunk->data = (unsigned char *) + obstack_alloc (state->chunk_obstack, new_size); + } + new_ptr = chunk->data + new_size; + + /* We do the relocations from back to front, because + thre relocations are in reverse order. */ + for (reloc = block->u.relocations; ; reloc = reloc->next) + { + /* Lower old index of piece to be copied with no relocation. */ + int start = reloc == NULL ? 0 + : reloc->kind == 0 ? reloc->offset + 4 + : reloc->offset + 2; + int32 value; + int new_offset; + int n = (old_ptr - old_buffer) - start; + new_ptr -= n; + old_ptr -= n; + if (n > 0) + bcopy (old_ptr, new_ptr, n); + if (old_ptr == old_buffer) + break; + + if (reloc->kind == 0) + { + old_ptr -= 4; + value = GET_u4 (old_ptr); + } + else + { + old_ptr -= 2; + value = GET_u2 (old_ptr); + } + new_offset = new_ptr - chunk->data - (reloc->kind == -1 ? 2 : 4); + value += reloc->label->pc - (block->pc + new_offset); + *--new_ptr = (unsigned char) value; value >>= 8; + *--new_ptr = (unsigned char) value; value >>= 8; + if (reloc->kind != -1) + { + *--new_ptr = (unsigned char) value; value >>= 8; + *--new_ptr = (unsigned char) value; + } + if (reloc->kind > 0) + { + /* Convert: OP TARGET to: OP_w TARGET; (OP is goto or jsr). */ + --old_ptr; + *--new_ptr = reloc->kind; + } + else if (reloc->kind < -1) + { + /* Convert: ifCOND TARGET to: ifNCOND T; goto_w TARGET; T: */ + --old_ptr; + *--new_ptr = OPCODE_goto_w; + *--new_ptr = 3; + *--new_ptr = 0; + *--new_ptr = - reloc->kind; + } + } + } + state->code_length = pc; +} + +void +init_jcf_state (state, work) + struct jcf_partial *state; + struct obstack *work; +{ + state->chunk_obstack = work; + state->first = state->chunk = NULL; + CPOOL_INIT (&state->cpool); + BUFFER_INIT (&state->localvars); + BUFFER_INIT (&state->bytecode); +} + +void +init_jcf_method (state, method) + struct jcf_partial *state; + tree method; +{ + state->current_method = method; + state->blocks = state->last_block = NULL; + state->linenumber_count = 0; + state->first_lvar = state->last_lvar = NULL; + state->lvar_count = 0; + state->labeled_blocks = NULL; + state->code_length = 0; + BUFFER_RESET (&state->bytecode); + BUFFER_RESET (&state->localvars); + state->code_SP = 0; + state->code_SP_max = 0; +} + +void +release_jcf_state (state) + struct jcf_partial *state; +{ + CPOOL_FINISH (&state->cpool); + obstack_free (state->chunk_obstack, state->first); +} + /* Generate and return a list of chunks containing the class CLAS in the .class file representation. The list can be written to a .class file using write_chunks. Allocate chunks from obstack WORK. */ -/* Currently does not write any attributes i.e. no code. */ - struct chunk * -generate_classfile (clas, work) +generate_classfile (clas, state) tree clas; - struct obstack *work; + struct jcf_partial *state; { - CPool cpool; - struct chunk head; - struct chunk *chunk; struct chunk *cpool_chunk; + char *source_file; char *ptr; int i; char *fields_count_ptr; int fields_count = 0; char *methods_count_ptr; int methods_count = 0; + static tree SourceFile_node = NULL_TREE; tree part; int total_supers = clas == object_type_node ? 0 : TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (clas)); - - chunk = alloc_chunk (&head, NULL, 8, work); - ptr = chunk->data; + + ptr = append_chunk (NULL, 8, state); PUT4 (0xCafeBabe); /* Magic number */ PUT2 (3); /* Minor version */ PUT2 (45); /* Major version */ - CPOOL_INIT(&cpool); - cpool_chunk = chunk = alloc_chunk (chunk, NULL, 0, work); + append_chunk (NULL, 0, state); + cpool_chunk = state->chunk; /* Next allocate the chunk containing acces_flags through fields_counr. */ if (clas == object_type_node) i = 10; else i = 8 + 2 * total_supers; - chunk = alloc_chunk (chunk, NULL, i, work); - ptr = chunk->data; + ptr = append_chunk (NULL, i, state); i = get_access_flags (TYPE_NAME (clas)); PUT2 (i); /* acces_flags */ - i = find_class_constant (&cpool, clas); PUT2 (i); /* this_class */ + i = find_class_constant (&state->cpool, clas); PUT2 (i); /* this_class */ if (clas == object_type_node) { PUT2(0); /* super_class */ @@ -791,12 +1683,13 @@ generate_classfile (clas, work) { tree basetypes = TYPE_BINFO_BASETYPES (clas); tree base = BINFO_TYPE (TREE_VEC_ELT (basetypes, 0)); - int j = find_class_constant (&cpool, base); PUT2 (j); /* super_class */ + int j = find_class_constant (&state->cpool, base); + PUT2 (j); /* super_class */ PUT2 (total_supers - 1); /* interfaces_count */ for (i = 1; i < total_supers; i++) { base = BINFO_TYPE (TREE_VEC_ELT (basetypes, i)); - j = find_class_constant (&cpool, base); + j = find_class_constant (&state->cpool, base); PUT2 (j); } } @@ -806,11 +1699,10 @@ generate_classfile (clas, work) { if (DECL_NAME (part) == NULL_TREE) continue; - chunk = alloc_chunk (chunk, NULL, 8, work); - ptr = chunk->data; + ptr = append_chunk (NULL, 8, state); i = get_access_flags (part); PUT2 (i); - i = find_utf8_constant (&cpool, DECL_NAME (part)); PUT2 (i); - i = find_utf8_constant (&cpool, build_java_signature (TREE_TYPE (part))); + i = find_utf8_constant (&state->cpool, DECL_NAME (part)); PUT2 (i); + i = find_utf8_constant (&state->cpool, build_java_signature (TREE_TYPE (part))); PUT2(i); PUT2 (0); /* attributes_count */ /* FIXME - emit ConstantValue attribute when appropriate */ @@ -818,120 +1710,141 @@ generate_classfile (clas, work) } ptr = fields_count_ptr; PUT2 (fields_count); - chunk = alloc_chunk (chunk, NULL, 2, work); - ptr = methods_count_ptr = chunk->data; + ptr = methods_count_ptr = append_chunk (NULL, 2, state); PUT2 (0); for (part = TYPE_METHODS (clas); part; part = TREE_CHAIN (part)) { + struct jcf_block *block; tree body = BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (part)); - int linenumber_size; /* 4 * number of line number entries */ - chunk = alloc_chunk (chunk, NULL, 8, work); - ptr = chunk->data; + tree name = DECL_CONSTRUCTOR_P (part) ? init_identifier_node + : DECL_NAME (part); + tree type = TREE_TYPE (part); + ptr = append_chunk (NULL, 8, state); i = get_access_flags (part); PUT2 (i); - i = find_utf8_constant (&cpool, DECL_NAME (part)); PUT2 (i); - i = find_utf8_constant (&cpool, build_java_signature (TREE_TYPE (part))); + i = find_utf8_constant (&state->cpool, name); PUT2 (i); + i = find_utf8_constant (&state->cpool, build_java_signature (type)); PUT2 (i); PUT2 (body != NULL_TREE ? 1 : 0); /* attributes_count */ if (body != NULL_TREE) { int code_attributes_count = 0; - int linenumber_size; /* 4 * number of line number entries */ - int localvartable_size; /* 10 * number of local variable entries */ static tree Code_node = NULL_TREE; tree t; char *attr_len_ptr; - int code_length; if (Code_node == NULL_TREE) Code_node = get_identifier ("Code"); - chunk = alloc_chunk (chunk, NULL, 14, work); - ptr = chunk->data; - i = find_utf8_constant (&cpool, Code_node); PUT2 (i); + ptr = append_chunk (NULL, 14, state); + i = find_utf8_constant (&state->cpool, Code_node); PUT2 (i); attr_len_ptr = ptr; - BUFFER_RESET (&bytecode); - BUFFER_RESET (&localvartable); - BUFFER_RESET (&linenumbers); - BUFFER_RESET (&localvars); - code_SP = 0; - code_SP_max = 0; - code_cpool = &cpool; + init_jcf_method (state, part); + get_jcf_label_here (state); /* Force a first block. */ for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t)) - localvar_alloc (t, 0); - generate_bytecode_insns (part, body, IGNORE_TARGET); - code_length = PC; + localvar_alloc (t, state); + generate_bytecode_insns (body, IGNORE_TARGET, state); for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t)) - localvar_free (t, code_length); - linenumber_size = BUFFER_LENGTH (&linenumbers); - localvartable_size = BUFFER_LENGTH (&localvartable); - chunk = alloc_chunk (chunk, NULL, code_length, work); - bcopy (bytecode.data, chunk->data, code_length); + localvar_free (t, state); + finish_jcf_block (state); + perform_relocations (state); + ptr = attr_len_ptr; - i = 8 + code_length + 4; - if (linenumber_size > 0) + i = 8 + state->code_length + 4; + if (state->linenumber_count > 0) { code_attributes_count++; - i += 8 + linenumber_size; + i += 8 + 4 * state->linenumber_count; } - if (localvartable_size > 0) + if (state->lvar_count > 0) { code_attributes_count++; - i += 8 + localvartable_size; + i += 8 + 10 * state->lvar_count; } PUT4 (i); /* attribute_length */ - PUT2 (code_SP_max); /* max_stack */ + PUT2 (state->code_SP_max); /* max_stack */ PUT2 (localvar_max); /* max_locals */ - PUT4 (code_length); - chunk = alloc_chunk (chunk, NULL, 4, work); - ptr = chunk->data; + PUT4 (state->code_length); + ptr = append_chunk (NULL, 4, state); PUT2 (0); /* exception_table_length */ PUT2 (code_attributes_count); /* Write the LineNumberTable attribute. */ - if (linenumber_size > 0) + if (state->linenumber_count > 0) { static tree LineNumberTable_node = NULL_TREE; - chunk = alloc_chunk (chunk, NULL, 8 + linenumber_size, work); - ptr = chunk->data; + ptr = append_chunk (NULL, 8 + 4 * state->linenumber_count, state); if (LineNumberTable_node == NULL_TREE) LineNumberTable_node = get_identifier ("LineNumberTable"); - i = find_utf8_constant (&cpool, LineNumberTable_node); + i = find_utf8_constant (&state->cpool, LineNumberTable_node); PUT2 (i); /* attribute_name_index */ - i = 2 + linenumber_size; PUT4 (i); /* attribute_length */ - i = linenumber_size >> 2; PUT2 (i); - PUTN (linenumbers.data, linenumber_size); + i = 2+4*state->linenumber_count; PUT4(i); /* attribute_length */ + i = state->linenumber_count; PUT2 (i); + for (block = state->blocks; block != NULL; block = block->next) + { + int line = block->linenumber; + if (line > 0) + { + PUT2 (block->pc); + PUT2 (line); + } + } } /* Write the LocalVariableTable attribute. */ - if (localvartable_size > 0) + if (state->lvar_count > 0) { static tree LocalVariableTable_node = NULL_TREE; - chunk = alloc_chunk (chunk, NULL, 8 + localvartable_size, work); - ptr = chunk->data; + struct localvar_info *lvar = state->first_lvar; + ptr = append_chunk (NULL, 8 + 10 * state->lvar_count, state); if (LocalVariableTable_node == NULL_TREE) LocalVariableTable_node = get_identifier("LocalVariableTable"); - i = find_utf8_constant (&cpool, LocalVariableTable_node); + i = find_utf8_constant (&state->cpool, LocalVariableTable_node); PUT2 (i); /* attribute_name_index */ - i = 2 + localvartable_size; PUT4 (i); /* attribute_length */ - i = localvartable_size / 10; PUT2 (i); - PUTN (localvartable.data, localvartable_size); + i = 2 + 10 * state->lvar_count; PUT4 (i); /* attribute_length */ + i = state->lvar_count; PUT2 (i); + for ( ; lvar != NULL; lvar = lvar->next) + { + tree name = DECL_NAME (lvar->decl); + tree sig = build_java_signature (TREE_TYPE (lvar->decl)); + i = lvar->start_label->pc; PUT2 (i); + i = lvar->end_label->pc - i; PUT2 (i); + i = find_utf8_constant (&state->cpool, name); PUT2 (i); + i = find_utf8_constant (&state->cpool, sig); PUT2 (i); + i = DECL_LOCAL_INDEX (lvar->decl); PUT2 (i); + } } } methods_count++; } ptr = methods_count_ptr; PUT2 (methods_count); - chunk = alloc_chunk (chunk, NULL, 2, work); - ptr = chunk->data; - PUT2 (0); /* attributes_count */ + source_file = DECL_SOURCE_FILE (TYPE_NAME (clas)); + for (ptr = source_file; ; ptr++) + { + char ch = *ptr; + if (ch == '\0') + break; + if (ch == '/' || ch == '\\') + source_file = ptr+1; + } + ptr = append_chunk (NULL, 10, state); + PUT2 (1); /* attributes_count */ + + /* generate the SourceFile attribute. */ + if (SourceFile_node == NULL_TREE) + SourceFile_node = get_identifier ("SourceFile"); + i = find_utf8_constant (&state->cpool, SourceFile_node); + PUT2 (i); /* attribute_name_index */ + PUT4 (2); + i = find_utf8_constant (&state->cpool, get_identifier (source_file)); + PUT2 (i); /* New finally generate the contents of the constant pool chunk. */ - i = count_constant_pool_bytes (&cpool); - ptr = obstack_alloc (work, i); + i = count_constant_pool_bytes (&state->cpool); + ptr = obstack_alloc (state->chunk_obstack, i); cpool_chunk->data = ptr; cpool_chunk->size = i; - write_constant_pool (&cpool, ptr, i); - CPOOL_FINISH (&cpool); - return head.next; + write_constant_pool (&state->cpool, ptr, i); + return state->first; } char* @@ -951,14 +1864,16 @@ write_classfile (clas) tree clas; { struct obstack *work = &temporary_obstack; + struct jcf_partial state[1]; char *class_file_name = make_class_file_name (clas); struct chunk *chunks; FILE* stream = fopen (class_file_name, "wb"); if (stream == NULL) fatal ("failed to open `%s' for writing", class_file_name); - chunks = generate_classfile (clas, work); + init_jcf_state (state, work); + chunks = generate_classfile (clas, state); write_chunks (stream, chunks); if (fclose (stream)) fatal ("failed to close after writing `%s'", class_file_name); - obstack_free (work, chunks); + release_jcf_state (state); } |