Commit 3885dfa7 by Per Bothner

*** empty log message ***

From-SVN: r23619
parent 421fb085
...@@ -23,6 +23,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ ...@@ -23,6 +23,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include <string.h>
#include "tree.h" #include "tree.h"
#include "java-tree.h" #include "java-tree.h"
#include "jcf.h" #include "jcf.h"
...@@ -112,6 +113,8 @@ struct jcf_block ...@@ -112,6 +113,8 @@ struct jcf_block
int linenumber; int linenumber;
/* After finish_jcf_block is called, The actual instructions contained in this block.
Before than NULL, and the instructions are in state->bytecode. */
struct chunk *chunk; struct chunk *chunk;
union { union {
...@@ -124,20 +127,27 @@ struct jcf_block ...@@ -124,20 +127,27 @@ struct jcf_block
} u; } u;
}; };
#define SWITCH_ALIGN_RELOC 4
#define BLOCK_START_RELOC 1
struct jcf_relocation struct jcf_relocation
{ {
/* Next relocation for the current jcf_block. */ /* Next relocation for the current jcf_block. */
struct jcf_relocation *next; struct jcf_relocation *next;
/* The (byte) offset within the current block that needs to be relocated. */ /* The (byte) offset within the current block that needs to be relocated. */
int offset; HOST_WIDE_INT offset;
/* 0 if offset is a 4-byte relative offset. /* 0 if offset is a 4-byte relative offset.
4 (SWITCH_ALIGN_RELOC) if offset points to 0-3 padding bytes inserted
for proper alignment in tableswitch/lookupswitch instructions.
1 (BLOCK_START_RELOC) if offset points to a 4-byte offset relative
to the start of the containing block.
-1 if offset is a 2-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 < -1 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 that does not have a corresponding 4-byte offset version, in which
case the absolute value of kind is the inverted opcode. case the absolute value of kind is the inverted opcode.
> 0 if offset is the address of an instruction (such as jsr) with a > 4 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, 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). */ in which case kind is the opcode of the 4-byte version (such as jsr_w). */
int kind; int kind;
...@@ -146,6 +156,32 @@ struct jcf_relocation ...@@ -146,6 +156,32 @@ struct jcf_relocation
struct jcf_block *label; struct jcf_block *label;
}; };
/* State for single catch clause. */
struct jcf_handler
{
struct jcf_handler *next;
struct jcf_block *start_label;
struct jcf_block *end_label;
struct jcf_block *handler_label;
/* The sub-class of Throwable handled, or NULL_TREE (for finally). */
tree type;
};
/* State for the current switch statement. */
struct jcf_switch_state
{
struct jcf_switch_state *prev;
struct jcf_block *default_label;
struct jcf_relocation *cases;
int num_cases;
HOST_WIDE_INT min_case, max_case;
};
/* This structure is used to contain the various pieces that will /* This structure is used to contain the various pieces that will
become a .class file. */ become a .class file. */
...@@ -186,6 +222,18 @@ struct jcf_partial ...@@ -186,6 +222,18 @@ struct jcf_partial
/* The buffer allocated for bytecode for the current jcf_block. */ /* The buffer allocated for bytecode for the current jcf_block. */
struct buffer bytecode; struct buffer bytecode;
/* Chain of exception handlers for the current method. */
struct jcf_handler *handlers;
/* Last element in handlers chain. */
struct jcf_handler *last_handler;
/* Number of exception handlers for the current method. */
int num_handlers;
/* Information about the current switch statemenet. */
struct jcf_switch_state *sw_state;
}; };
static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *)); static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *));
...@@ -197,7 +245,7 @@ static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *)); ...@@ -197,7 +245,7 @@ static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *));
#define PUT1(X) (*ptr++ = (X)) #define PUT1(X) (*ptr++ = (X))
#define PUT2(X) (PUT1((X) >> 8), PUT1((X) & 0xFF)) #define PUT2(X) (PUT1((X) >> 8), PUT1((X) & 0xFF))
#define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF)) #define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF))
#define PUTN(P, N) (bcopy(P, ptr, N), ptr += (N)) #define PUTN(P, N) (memcpy(ptr, P, N), ptr += (N))
/* Allocate a new chunk on obstack WORK, and link it in after LAST. /* Allocate a new chunk on obstack WORK, and link it in after LAST.
...@@ -244,7 +292,7 @@ append_chunk_copy (data, size, state) ...@@ -244,7 +292,7 @@ append_chunk_copy (data, size, state)
struct jcf_partial *state; struct jcf_partial *state;
{ {
unsigned char *ptr = append_chunk (NULL, size, state); unsigned char *ptr = append_chunk (NULL, size, state);
bcopy (data, ptr, size); memcpy (ptr, data, size);
} }
struct jcf_block * struct jcf_block *
...@@ -265,9 +313,9 @@ finish_jcf_block (state) ...@@ -265,9 +313,9 @@ finish_jcf_block (state)
{ {
struct jcf_block *block = state->last_block; struct jcf_block *block = state->last_block;
struct jcf_relocation *reloc; struct jcf_relocation *reloc;
int code_length = BUFFER_LENGTH (&state->bytecode);
int pc = state->code_length; int pc = state->code_length;
append_chunk_copy (state->bytecode.data, BUFFER_LENGTH (&state->bytecode), append_chunk_copy (state->bytecode.data, code_length, state);
state);
BUFFER_RESET (&state->bytecode); BUFFER_RESET (&state->bytecode);
block->chunk = state->chunk; block->chunk = state->chunk;
...@@ -276,7 +324,9 @@ finish_jcf_block (state) ...@@ -276,7 +324,9 @@ finish_jcf_block (state)
for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next)
{ {
int kind = reloc->kind; int kind = reloc->kind;
if (kind > 0) if (kind == SWITCH_ALIGN_RELOC)
pc += 3;
else if (kind > BLOCK_START_RELOC)
pc += 2; /* 2-byte offset may grow to 4-byte offset */ pc += 2; /* 2-byte offset may grow to 4-byte offset */
else if (kind < -1) else if (kind < -1)
pc += 5; /* May need to add a goto_w. */ pc += 5; /* May need to add a goto_w. */
...@@ -326,6 +376,30 @@ put_linenumber (line, state) ...@@ -326,6 +376,30 @@ put_linenumber (line, state)
state->linenumber_count++; state->linenumber_count++;
} }
/* Allocate a new jcf_handler, for a catch clause that catches exceptions
in the range (START_LABEL, END_LABEL). */
static struct jcf_handler *
alloc_handler (start_label, end_label, state)
struct jcf_block *start_label;
struct jcf_block *end_label;
struct jcf_partial *state;
{
struct jcf_handler *handler = (struct jcf_handler *)
obstack_alloc (state->chunk_obstack, sizeof (struct jcf_handler));
handler->start_label = start_label;
handler->end_label = end_label;
handler->handler_label = get_jcf_label_here (state);
if (state->handlers == NULL)
state->handlers = handler;
else
state->last_handler->next = handler;
state->last_handler = handler;
handler->next = NULL;
state->num_handlers++;
return handler;
}
/* The index of jvm local variable allocated for this DECL. /* The index of jvm local variable allocated for this DECL.
This is assigned when generating .class files; This is assigned when generating .class files;
...@@ -347,7 +421,7 @@ struct localvar_info ...@@ -347,7 +421,7 @@ struct localvar_info
#define localvar_max \ #define localvar_max \
((struct localvar_info**) state->localvars.ptr - localvar_buffer) ((struct localvar_info**) state->localvars.ptr - localvar_buffer)
void int
localvar_alloc (decl, state) localvar_alloc (decl, state)
tree decl; tree decl;
struct jcf_partial *state; struct jcf_partial *state;
...@@ -403,6 +477,7 @@ localvar_free (decl, state) ...@@ -403,6 +477,7 @@ localvar_free (decl, state)
register struct localvar_info **ptr = &localvar_buffer [index]; register struct localvar_info **ptr = &localvar_buffer [index];
register struct localvar_info *info = *ptr; register struct localvar_info *info = *ptr;
int wide = TYPE_IS_WIDE (TREE_TYPE (decl)); int wide = TYPE_IS_WIDE (TREE_TYPE (decl));
int i;
info->end_label = end_label; info->end_label = end_label;
...@@ -493,6 +568,7 @@ push_constant1 (index, state) ...@@ -493,6 +568,7 @@ push_constant1 (index, state)
int index; int index;
struct jcf_partial *state; struct jcf_partial *state;
{ {
RESERVE (3);
if (index < 256) if (index < 256)
{ {
OP1 (OPCODE_ldc); OP1 (OPCODE_ldc);
...@@ -540,7 +616,7 @@ push_int_const (i, state) ...@@ -540,7 +616,7 @@ push_int_const (i, state)
else else
{ {
i = find_constant1 (&state->cpool, CONSTANT_Integer, i & 0xFFFFFFFF); i = find_constant1 (&state->cpool, CONSTANT_Integer, i & 0xFFFFFFFF);
push_constant1 (i); push_constant1 (i, state);
} }
} }
...@@ -557,21 +633,19 @@ push_long_const (lo, hi, state) ...@@ -557,21 +633,19 @@ push_long_const (lo, hi, state)
RESERVE(1); RESERVE(1);
OP1(OPCODE_lconst_0 + lo); OP1(OPCODE_lconst_0 + lo);
} }
#if 0 else if ((hi == 0 && lo < 32768) || (hi == -1 && lo >= -32768))
else if ((jlong) (jint) i == i)
{ {
push_int_const ((jint) i, state); push_int_const (lo, state);
RESERVE (1); RESERVE (1);
OP1 (OPCODE_i2l); OP1 (OPCODE_i2l);
} }
#endif
else else
{ {
HOST_WIDE_INT w1, w2; HOST_WIDE_INT w1, w2;
lshift_double (lo, hi, -32, 64, &w1, &w2, 1); lshift_double (lo, hi, -32, 64, &w1, &w2, 1);
hi = find_constant1 (&state->cpool, CONSTANT_Long, hi = find_constant2 (&state->cpool, CONSTANT_Long,
w1 & 0xFFFFFFFF, lo & 0xFFFFFFFF); w1 & 0xFFFFFFFF, lo & 0xFFFFFFFF);
push_constant2 (hi); push_constant2 (hi, state);
} }
} }
...@@ -592,22 +666,23 @@ field_op (field, opcode, state) ...@@ -592,22 +666,23 @@ field_op (field, opcode, state)
opcodes typically depend on the operand type. */ opcodes typically depend on the operand type. */
int int
adjust_typed_op (type) adjust_typed_op (type, max)
tree type; tree type;
int max;
{ {
switch (TREE_CODE (type)) switch (TREE_CODE (type))
{ {
case POINTER_TYPE: case POINTER_TYPE:
case RECORD_TYPE: return 4; case RECORD_TYPE: return 4;
case BOOLEAN_TYPE: case BOOLEAN_TYPE:
return TYPE_PRECISION (type) == 32 ? 0 : 5; return TYPE_PRECISION (type) == 32 || max < 5 ? 0 : 5;
case CHAR_TYPE: case CHAR_TYPE:
return TYPE_PRECISION (type) == 32 ? 0 : 6; return TYPE_PRECISION (type) == 32 || max < 6 ? 0 : 6;
case INTEGER_TYPE: case INTEGER_TYPE:
switch (TYPE_PRECISION (type)) switch (TYPE_PRECISION (type))
{ {
case 8: return 5; case 8: return max < 5 ? 0 : 5;
case 16: return 7; case 16: return max < 7 ? 0 : 7;
case 32: return 0; case 32: return 0;
case 64: return 1; case 64: return 1;
} }
...@@ -619,6 +694,8 @@ adjust_typed_op (type) ...@@ -619,6 +694,8 @@ adjust_typed_op (type)
case 64: return 3; case 64: return 3;
} }
break; break;
default:
break;
} }
abort (); abort ();
} }
...@@ -705,11 +782,12 @@ emit_iinc (var, value, state) ...@@ -705,11 +782,12 @@ emit_iinc (var, value, state)
static void static void
emit_load_or_store (var, opcode, state) emit_load_or_store (var, opcode, state)
tree var; tree var; /* Variable to load from or store into. */
int opcode; /* Either OPCODE_iload or OPCODE_istore. */
struct jcf_partial *state; struct jcf_partial *state;
{ {
tree type = TREE_TYPE (var); tree type = TREE_TYPE (var);
int kind = adjust_typed_op (type); int kind = adjust_typed_op (type, 4);
int index = DECL_LOCAL_INDEX (var); int index = DECL_LOCAL_INDEX (var);
if (index <= 3) if (index <= 3)
{ {
...@@ -717,7 +795,7 @@ emit_load_or_store (var, opcode, state) ...@@ -717,7 +795,7 @@ emit_load_or_store (var, opcode, state)
OP1 (opcode + 5 + 4 * kind + index); /* [ilfda]{load,store}_[0123] */ OP1 (opcode + 5 + 4 * kind + index); /* [ilfda]{load,store}_[0123] */
} }
else else
maybe_wide (opcode + kind, index); /* [ilfda]{load,store} */ maybe_wide (opcode + kind, index, state); /* [ilfda]{load,store} */
} }
static void static void
...@@ -739,6 +817,17 @@ emit_store (var, state) ...@@ -739,6 +817,17 @@ emit_store (var, state)
} }
static void static void
emit_unop (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);
}
static void
emit_binop (opcode, type, state) emit_binop (opcode, type, state)
enum java_opcode opcode; enum java_opcode opcode;
tree type; tree type;
...@@ -750,13 +839,11 @@ emit_binop (opcode, type, state) ...@@ -750,13 +839,11 @@ emit_binop (opcode, type, state)
NOTE_POP (size); NOTE_POP (size);
} }
/* Emit a conditional jump to TARGET with a 2-byte relative jump offset static struct jcf_relocation *
The opcode is OPCODE, the inverted opcode is INV_OPCODE. */ emit_reloc (value, kind, target, state)
HOST_WIDE_INT value;
static void int kind;
emit_if (target, opcode, inv_opcode, state)
struct jcf_block *target; struct jcf_block *target;
int opcode, inv_opcode;
struct jcf_partial *state; struct jcf_partial *state;
{ {
struct jcf_relocation *reloc = (struct jcf_relocation *) struct jcf_relocation *reloc = (struct jcf_relocation *)
...@@ -764,29 +851,51 @@ emit_if (target, opcode, inv_opcode, state) ...@@ -764,29 +851,51 @@ emit_if (target, opcode, inv_opcode, state)
struct jcf_block *block = state->last_block; struct jcf_block *block = state->last_block;
reloc->next = block->u.relocations; reloc->next = block->u.relocations;
block->u.relocations = reloc; block->u.relocations = reloc;
OP1 (opcode);
reloc->offset = BUFFER_LENGTH (&state->bytecode); reloc->offset = BUFFER_LENGTH (&state->bytecode);
OP2 (1); // 1 byte from reloc back to start of instruction.
reloc->kind = - inv_opcode;
reloc->label = target; reloc->label = target;
reloc->kind = kind;
if (kind == 0 || kind == BLOCK_START_RELOC)
OP4 (value);
else if (kind != SWITCH_ALIGN_RELOC)
OP2 (value);
} }
static void static void
emit_goto_or_jsr (target, opcode, opcode_w, state) emit_switch_reloc (label, state)
struct jcf_block *target; struct jcf_block *label;
int opcode, opcode_w; struct jcf_partial *state;
{
emit_reloc (0, BLOCK_START_RELOC, label, state);
}
/* Similar to emit_switch_reloc,
but re-uses an existing case reloc. */
static void
emit_case_reloc (reloc, state)
struct jcf_relocation *reloc;
struct jcf_partial *state; 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; struct jcf_block *block = state->last_block;
reloc->next = block->u.relocations; reloc->next = block->u.relocations;
block->u.relocations = reloc; block->u.relocations = reloc;
OP1 (opcode);
reloc->offset = BUFFER_LENGTH (&state->bytecode); reloc->offset = BUFFER_LENGTH (&state->bytecode);
OP2 (1); // 1 byte from reloc back to start of instruction. reloc->kind = BLOCK_START_RELOC;
reloc->kind = opcode_w; OP4 (0);
reloc->label = target; }
/* 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;
{
OP1 (opcode);
// value is 1 byte from reloc back to start of instruction.
emit_reloc (1, - inv_opcode, target, state);
} }
static void static void
...@@ -794,7 +903,9 @@ emit_goto (target, state) ...@@ -794,7 +903,9 @@ emit_goto (target, state)
struct jcf_block *target; struct jcf_block *target;
struct jcf_partial *state; struct jcf_partial *state;
{ {
emit_goto_or_jsr (target, OPCODE_goto, OPCODE_goto_w, state); OP1 (OPCODE_goto);
// Value is 1 byte from reloc back to start of instruction.
emit_reloc (1, OPCODE_goto_w, target, state);
} }
static void static void
...@@ -802,7 +913,9 @@ emit_jsr (target, state) ...@@ -802,7 +913,9 @@ emit_jsr (target, state)
struct jcf_block *target; struct jcf_block *target;
struct jcf_partial *state; struct jcf_partial *state;
{ {
emit_goto_or_jsr (target, OPCODE_jsr, OPCODE_jsr_w, state); OP1 (OPCODE_jsr);
// Value is 1 byte from reloc back to start of instruction.
emit_reloc (1, OPCODE_jsr_w, target, state);
} }
/* Generate code to evaluate EXP. If the result is true, /* Generate code to evaluate EXP. If the result is true,
...@@ -850,6 +963,10 @@ generate_bytecode_conditional (exp, true_label, false_label, ...@@ -850,6 +963,10 @@ generate_bytecode_conditional (exp, true_label, false_label,
fatal ("internal error non-matching SP"); fatal ("internal error non-matching SP");
} }
break; break;
case TRUTH_NOT_EXPR:
generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label, true_label,
! true_branch_first, state);
break;
case TRUTH_ANDIF_EXPR: case TRUTH_ANDIF_EXPR:
{ {
struct jcf_block *next_label = gen_jcf_label (state); struct jcf_block *next_label = gen_jcf_label (state);
...@@ -915,6 +1032,7 @@ generate_bytecode_conditional (exp, true_label, false_label, ...@@ -915,6 +1032,7 @@ generate_bytecode_conditional (exp, true_label, false_label,
type = TREE_TYPE (exp0); type = TREE_TYPE (exp0);
switch (TREE_CODE (type)) switch (TREE_CODE (type))
{ {
int opf;
case POINTER_TYPE: case RECORD_TYPE: case POINTER_TYPE: case RECORD_TYPE:
switch (TREE_CODE (exp)) switch (TREE_CODE (exp))
{ {
...@@ -936,7 +1054,22 @@ generate_bytecode_conditional (exp, true_label, false_label, ...@@ -936,7 +1054,22 @@ generate_bytecode_conditional (exp, true_label, false_label,
NOTE_POP (2); NOTE_POP (2);
goto compare_2; goto compare_2;
case REAL_TYPE: case REAL_TYPE:
fatal ("float comparison not implemented"); generate_bytecode_insns (exp0, STACK_TARGET, state);
generate_bytecode_insns (exp1, STACK_TARGET, state);
if (op == OPCODE_if_icmplt || op == op == OPCODE_if_icmple)
opf = OPCODE_fcmpg;
else
opf = OPCODE_fcmpl;
if (TYPE_PRECISION (type) > 32)
{
opf += 2;
NOTE_POP (4);
}
else
NOTE_POP (2);
RESERVE (1);
OP1 (opf);
goto compare_1;
case INTEGER_TYPE: case INTEGER_TYPE:
if (TYPE_PRECISION (type) > 32) if (TYPE_PRECISION (type) > 32)
{ {
...@@ -967,6 +1100,8 @@ generate_bytecode_conditional (exp, true_label, false_label, ...@@ -967,6 +1100,8 @@ generate_bytecode_conditional (exp, true_label, false_label,
case OPCODE_if_icmple: case OPCODE_if_icmple:
op -= 2; op -= 2;
break; break;
default:
break;
} }
generate_bytecode_insns (exp1, STACK_TARGET, state); generate_bytecode_insns (exp1, STACK_TARGET, state);
NOTE_POP (1); NOTE_POP (1);
...@@ -994,7 +1129,7 @@ generate_bytecode_conditional (exp, true_label, false_label, ...@@ -994,7 +1129,7 @@ generate_bytecode_conditional (exp, true_label, false_label,
break; break;
} }
if (save_SP != state->code_SP) if (save_SP != state->code_SP)
fatal ("inetrnal error - SP mismatch"); fatal ("internal error - SP mismatch");
} }
/* Generate bytecode for sub-expression EXP of METHOD. /* Generate bytecode for sub-expression EXP of METHOD.
...@@ -1025,13 +1160,20 @@ generate_bytecode_insns (exp, target, state) ...@@ -1025,13 +1160,20 @@ generate_bytecode_insns (exp, target, state)
if (BLOCK_EXPR_BODY (exp)) if (BLOCK_EXPR_BODY (exp))
{ {
tree local; tree local;
tree body = BLOCK_EXPR_BODY (exp);
for (local = BLOCK_EXPR_DECLS (exp); local; ) for (local = BLOCK_EXPR_DECLS (exp); local; )
{ {
tree next = TREE_CHAIN (local); tree next = TREE_CHAIN (local);
localvar_alloc (local, state); localvar_alloc (local, state);
local = next; local = next;
} }
generate_bytecode_insns (BLOCK_EXPR_BODY (exp), target, state); /* Avoid deep recursion for long blocks. */
while (TREE_CODE (body) == COMPOUND_EXPR)
{
generate_bytecode_insns (TREE_OPERAND (body, 0), target, state);
body = TREE_OPERAND (body, 1);
}
generate_bytecode_insns (body, target, state);
for (local = BLOCK_EXPR_DECLS (exp); local; ) for (local = BLOCK_EXPR_DECLS (exp); local; )
{ {
tree next = TREE_CHAIN (local); tree next = TREE_CHAIN (local);
...@@ -1079,6 +1221,32 @@ generate_bytecode_insns (exp, target, state) ...@@ -1079,6 +1221,32 @@ generate_bytecode_insns (exp, target, state)
NOTE_PUSH (2); NOTE_PUSH (2);
} }
break; break;
case REAL_CST:
switch (TYPE_PRECISION (type))
{
long words[2];
int index;
case 32:
words[0] = etarsingle (TREE_REAL_CST (exp)) & 0xFFFFFFFF;
index = find_constant1 (&state->cpool, CONSTANT_Float, words[0]);
push_constant1 (index, state);
NOTE_PUSH (1);
break;
case 64:
etardouble (TREE_REAL_CST (exp), words);
index = find_constant2 (&state->cpool, CONSTANT_Double,
words[1-FLOAT_WORDS_BIG_ENDIAN] & 0xFFFFFFFF,
words[FLOAT_WORDS_BIG_ENDIAN] & 0xFFFFFFFF);
push_constant2 (index, state);
NOTE_PUSH (2);
break;
default:
abort ();
}
break;
case STRING_CST:
push_constant1 (find_string_constant (&state->cpool, exp), state);
break;
case VAR_DECL: case VAR_DECL:
if (TREE_STATIC (exp)) if (TREE_STATIC (exp))
{ {
...@@ -1090,6 +1258,7 @@ generate_bytecode_insns (exp, target, state) ...@@ -1090,6 +1258,7 @@ generate_bytecode_insns (exp, target, state)
case PARM_DECL: case PARM_DECL:
emit_load (exp, state); emit_load (exp, state);
break; break;
case NON_LVALUE_EXPR:
case INDIRECT_REF: case INDIRECT_REF:
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state);
break; break;
...@@ -1098,10 +1267,11 @@ generate_bytecode_insns (exp, target, state) ...@@ -1098,10 +1267,11 @@ generate_bytecode_insns (exp, target, state)
generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
if (target != IGNORE_TARGET) if (target != IGNORE_TARGET)
{ {
jopcode = OPCODE_iaload + adjust_typed_op (type); jopcode = OPCODE_iaload + adjust_typed_op (type, 7);
RESERVE(1); RESERVE(1);
OP1 (jopcode); OP1 (jopcode);
NOTE_POP (2); if (! TYPE_IS_WIDE (type))
NOTE_POP (1);
} }
break; break;
case COMPONENT_REF: case COMPONENT_REF:
...@@ -1162,12 +1332,186 @@ generate_bytecode_insns (exp, target, state) ...@@ -1162,12 +1332,186 @@ generate_bytecode_insns (exp, target, state)
then_label, else_label, 1, state); then_label, else_label, 1, state);
define_jcf_label (then_label, state); define_jcf_label (then_label, state);
generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
if (CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 1))
/* Not all expressions have CAN_COMPLETE_NORMALLY set properly. */
|| TREE_CODE (TREE_TYPE (exp)) != VOID_TYPE)
emit_goto (end_label, state); emit_goto (end_label, state);
define_jcf_label (else_label, state); define_jcf_label (else_label, state);
generate_bytecode_insns (TREE_OPERAND (exp, 2), target, state); generate_bytecode_insns (TREE_OPERAND (exp, 2), target, state);
define_jcf_label (end_label, state); define_jcf_label (end_label, state);
} }
break; break;
case CASE_EXPR:
{
struct jcf_switch_state *sw_state = state->sw_state;
struct jcf_relocation *reloc = (struct jcf_relocation *)
obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
HOST_WIDE_INT case_value = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0));
reloc->kind = 0;
reloc->label = get_jcf_label_here (state);
reloc->offset = case_value;
reloc->next = sw_state->cases;
sw_state->cases = reloc;
if (sw_state->num_cases == 0)
{
sw_state->min_case = case_value;
sw_state->max_case = case_value;
}
else
{
if (case_value < sw_state->min_case)
sw_state->min_case = case_value;
if (case_value > sw_state->max_case)
sw_state->max_case = case_value;
}
sw_state->num_cases++;
}
break;
case DEFAULT_EXPR:
state->sw_state->default_label = get_jcf_label_here (state);
break;
case SWITCH_EXPR:
{
/* The SWITCH_EXPR has three parts, generated in the following order:
1. the switch_expression (the value used to select the correct case);
2. the switch_body;
3. the switch_instruction (the tableswitch/loopupswitch instruction.).
After code generation, we will re-order then in the order 1, 3, 2.
This is to avoid an extra GOTOs. */
struct jcf_switch_state sw_state;
struct jcf_block *expression_last; /* Last block of the switch_expression. */
struct jcf_block *body_last; /* Last block of the switch_body. */
struct jcf_block *switch_instruction; /* First block of switch_instruction. */
struct jcf_block *instruction_last; /* Last block of the switch_instruction. */
struct jcf_block *body_block;
int switch_length;
sw_state.prev = state->sw_state;
state->sw_state = &sw_state;
sw_state.cases = NULL;
sw_state.num_cases = 0;
sw_state.default_label = NULL;
generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state);
expression_last = state->last_block;
body_block = get_jcf_label_here (state); /* Force a new block here. */
generate_bytecode_insns (TREE_OPERAND (exp, 1), IGNORE_TARGET, state);
body_last = state->last_block;
if (sw_state.default_label == NULL)
sw_state.default_label = gen_jcf_label (state);
switch_instruction = get_jcf_label_here (state);
if (sw_state.num_cases <= 1)
{
if (sw_state.num_cases == 0)
{
emit_pop (1, state);
NOTE_POP (1);
}
else
{
push_int_const (sw_state.cases->offset, state);
emit_if (sw_state.cases->label,
OPCODE_ifeq, OPCODE_ifne, state);
}
emit_goto (sw_state.default_label, state);
}
else
{
HOST_WIDE_INT i;
/* Copy the chain of relocs into a sorted array. */
struct jcf_relocation **relocs = (struct jcf_relocation **)
xmalloc (sw_state.num_cases * sizeof (struct jcf_relocation *));
/* The relocs arrays is a buffer with a gap.
The assumption is that cases will normally come in "runs". */
int gap_start = 0;
int gap_end = sw_state.num_cases;
struct jcf_relocation *reloc;
for (reloc = sw_state.cases; reloc != NULL; reloc = reloc->next)
{
HOST_WIDE_INT case_value = reloc->offset;
while (gap_end < sw_state.num_cases)
{
struct jcf_relocation *end = relocs[gap_end];
if (case_value <= end->offset)
break;
relocs[gap_start++] = end;
gap_end++;
}
while (gap_start > 0)
{
struct jcf_relocation *before = relocs[gap_start-1];
if (case_value >= before->offset)
break;
relocs[--gap_end] = before;
gap_start--;
}
relocs[gap_start++] = reloc;
/* Note we don't check for duplicates. FIXME! */
}
if (2 * sw_state.num_cases
>= sw_state.max_case - sw_state.min_case)
{ /* Use tableswitch. */
int index = 0;
RESERVE (13 + 4 * (sw_state.max_case - sw_state.min_case + 1));
OP1 (OPCODE_tableswitch);
emit_reloc (0, SWITCH_ALIGN_RELOC, NULL, state);
emit_switch_reloc (sw_state.default_label, state);
OP4 (sw_state.min_case);
OP4 (sw_state.max_case);
for (i = sw_state.min_case; ; )
{
if (i == sw_state.min_case + index)
emit_case_reloc (relocs[index++], state);
else
emit_switch_reloc (sw_state.default_label, state);
if (i == sw_state.max_case)
break;
i++;
}
}
else
{ /* Use lookupswitch. */
RESERVE(9 + 8 * sw_state.num_cases);
OP1 (OPCODE_lookupswitch);
emit_reloc (0, SWITCH_ALIGN_RELOC, NULL, state);
emit_switch_reloc (sw_state.default_label, state);
OP4 (sw_state.num_cases);
for (i = 0; i < sw_state.num_cases; i++)
{
struct jcf_relocation *reloc = relocs[i];
OP4 (reloc->offset);
emit_case_reloc (reloc, state);
}
}
free (relocs);
}
instruction_last = state->last_block;
if (sw_state.default_label->pc < 0)
define_jcf_label (sw_state.default_label, state);
else /* Force a new block. */
sw_state.default_label = get_jcf_label_here (state);
/* Now re-arrange the blocks so the switch_instruction
comes before the switch_body. */
switch_length = state->code_length - switch_instruction->pc;
switch_instruction->pc = body_block->pc;
instruction_last->next = body_block;
instruction_last->chunk->next = body_block->chunk;
expression_last->next = switch_instruction;
expression_last->chunk->next = switch_instruction->chunk;
body_last->next = sw_state.default_label;
body_last->chunk->next = NULL;
state->chunk = body_last->chunk;
for (; body_block != sw_state.default_label; body_block = body_block->next)
body_block->pc += switch_length;
free (sw_state.cases);
state->sw_state = sw_state.prev;
break;
}
case RETURN_EXPR: case RETURN_EXPR:
if (!TREE_OPERAND (exp, 0)) if (!TREE_OPERAND (exp, 0))
op = OPCODE_return; op = OPCODE_return;
...@@ -1177,7 +1521,7 @@ generate_bytecode_insns (exp, target, state) ...@@ -1177,7 +1521,7 @@ generate_bytecode_insns (exp, target, state)
if (TREE_CODE (exp) != MODIFY_EXPR) if (TREE_CODE (exp) != MODIFY_EXPR)
abort (); abort ();
exp = TREE_OPERAND (exp, 1); exp = TREE_OPERAND (exp, 1);
op = OPCODE_ireturn + adjust_typed_op (TREE_TYPE (exp)); op = OPCODE_ireturn + adjust_typed_op (TREE_TYPE (exp), 4);
generate_bytecode_insns (exp, STACK_TARGET, state); generate_bytecode_insns (exp, STACK_TARGET, state);
} }
RESERVE (1); RESERVE (1);
...@@ -1288,7 +1632,7 @@ generate_bytecode_insns (exp, target, state) ...@@ -1288,7 +1632,7 @@ generate_bytecode_insns (exp, target, state)
generate_bytecode_insns (TREE_OPERAND (exp, 1), STACK_TARGET, state); generate_bytecode_insns (TREE_OPERAND (exp, 1), STACK_TARGET, state);
emit_dup (2, 0, state); emit_dup (2, 0, state);
/* Stack: ..., array, index, array, index. */ /* Stack: ..., array, index, array, index. */
jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (exp)); jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (exp), 7);
RESERVE(1); RESERVE(1);
OP1 (jopcode); OP1 (jopcode);
NOTE_POP (2-size); NOTE_POP (2-size);
...@@ -1311,7 +1655,7 @@ generate_bytecode_insns (exp, target, state) ...@@ -1311,7 +1655,7 @@ generate_bytecode_insns (exp, target, state)
/* Stack, otherwise: ..., [result, ] oldvalue. */ /* Stack, otherwise: ..., [result, ] oldvalue. */
push_int_const (value, state); /* FIXME - assumes int! */ push_int_const (value, state); /* FIXME - assumes int! */
NOTE_PUSH (1); NOTE_PUSH (1);
emit_binop (OPCODE_iadd + adjust_typed_op (type), type, state); emit_binop (OPCODE_iadd + adjust_typed_op (type, 3), type, state);
if (target != IGNORE_TARGET && ! post_op) if (target != IGNORE_TARGET && ! post_op)
emit_dup (size, offset, state); emit_dup (size, offset, state);
/* Stack: ..., [result,] newvalue. */ /* Stack: ..., [result,] newvalue. */
...@@ -1399,7 +1743,7 @@ generate_bytecode_insns (exp, target, state) ...@@ -1399,7 +1743,7 @@ generate_bytecode_insns (exp, target, state)
else if (TREE_CODE (exp) == ARRAY_REF) else if (TREE_CODE (exp) == ARRAY_REF)
{ {
NOTE_POP (2); NOTE_POP (2);
jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp)); jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp), 7);
RESERVE(1); RESERVE(1);
OP1 (jopcode); OP1 (jopcode);
NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1);
...@@ -1408,49 +1752,289 @@ generate_bytecode_insns (exp, target, state) ...@@ -1408,49 +1752,289 @@ generate_bytecode_insns (exp, target, state)
fatal ("internal error (bad lhs to MODIFY_EXPR)"); fatal ("internal error (bad lhs to MODIFY_EXPR)");
break; break;
case PLUS_EXPR: case PLUS_EXPR:
jopcode = OPCODE_iadd + adjust_typed_op (type); jopcode = OPCODE_iadd;
goto binop; goto binop;
case MINUS_EXPR: case MINUS_EXPR:
jopcode = OPCODE_isub + adjust_typed_op (type); jopcode = OPCODE_isub;
goto binop; goto binop;
case MULT_EXPR: case MULT_EXPR:
jopcode = OPCODE_imul + adjust_typed_op (type); jopcode = OPCODE_imul;
goto binop; goto binop;
case TRUNC_DIV_EXPR: case TRUNC_DIV_EXPR:
case RDIV_EXPR: case RDIV_EXPR:
jopcode = OPCODE_idiv + adjust_typed_op (type); jopcode = OPCODE_idiv;
goto binop; goto binop;
case TRUNC_MOD_EXPR:
jopcode = OPCODE_irem;
goto binop;
case LSHIFT_EXPR: jopcode = OPCODE_ishl; goto binop;
case RSHIFT_EXPR: jopcode = OPCODE_ishr; goto binop;
case URSHIFT_EXPR: jopcode = OPCODE_iushr; goto binop;
case BIT_AND_EXPR: jopcode = OPCODE_iand; goto binop;
case BIT_IOR_EXPR: jopcode = OPCODE_ior; goto binop;
case BIT_XOR_EXPR: jopcode = OPCODE_ixor; goto binop;
binop: binop:
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); {
generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); tree arg0 = TREE_OPERAND (exp, 0);
tree arg1 = TREE_OPERAND (exp, 1);
jopcode += adjust_typed_op (type, 3);
if (arg0 == arg1 && TREE_CODE (arg0) == SAVE_EXPR)
{
/* fold may (e.g) convert 2*x to x+x. */
generate_bytecode_insns (TREE_OPERAND (arg0, 0), target, state);
emit_dup (TYPE_PRECISION (TREE_TYPE (arg0)) > 32 ? 2 : 1, 0, state);
}
else
{
generate_bytecode_insns (arg0, target, state);
generate_bytecode_insns (arg1, target, state);
}
if (target == STACK_TARGET) if (target == STACK_TARGET)
emit_binop (jopcode, type, state); emit_binop (jopcode, type, state);
break; break;
}
case TRUTH_NOT_EXPR:
case BIT_NOT_EXPR:
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state);
if (target == STACK_TARGET)
{
int is_long = TYPE_PRECISION (TREE_TYPE (exp)) > 32;
push_int_const (TREE_CODE (exp) == BIT_NOT_EXPR ? -1 : 1, state);
RESERVE (2);
if (is_long)
OP1 (OPCODE_i2l);
OP1 (OPCODE_ixor + is_long);
}
break;
case NEGATE_EXPR:
jopcode = OPCODE_ineg;
jopcode += adjust_typed_op (type, 3);
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state);
if (target == STACK_TARGET)
emit_unop (jopcode, type, state);
break;
case INSTANCEOF_EXPR:
{
int index = find_class_constant (&state->cpool, TREE_OPERAND (exp, 1));
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state);
RESERVE (3);
OP1 (OPCODE_instanceof);
OP2 (index);
}
break;
case CONVERT_EXPR:
case NOP_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
{
tree src = TREE_OPERAND (exp, 0);
tree src_type = TREE_TYPE (src);
tree dst_type = TREE_TYPE (exp);
generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state);
if (target == IGNORE_TARGET || src_type == dst_type)
break;
if (TREE_CODE (dst_type) == POINTER_TYPE)
{
if (TREE_CODE (exp) == CONVERT_EXPR)
{
int index = find_class_constant (&state->cpool, TREE_TYPE (dst_type));
RESERVE (3);
OP1 (OPCODE_checkcast);
OP2 (index);
}
}
else /* Convert numeric types. */
{
int wide_src = TYPE_PRECISION (src_type) > 32;
int wide_dst = TYPE_PRECISION (dst_type) > 32;
NOTE_POP (1 + wide_src);
RESERVE (1);
if (TREE_CODE (dst_type) == REAL_TYPE)
{
if (TREE_CODE (src_type) == REAL_TYPE)
OP1 (wide_dst ? OPCODE_f2d : OPCODE_d2f);
else if (TYPE_PRECISION (src_type) == 64)
OP1 (OPCODE_l2f + wide_dst);
else
OP1 (OPCODE_i2f + wide_dst);
}
else /* Convert to integral type. */
{
if (TREE_CODE (src_type) == REAL_TYPE)
OP1 (OPCODE_f2i + wide_dst + 3 * wide_src);
else if (wide_dst)
OP1 (OPCODE_i2l);
else if (wide_src)
OP1 (OPCODE_l2i);
if (TYPE_PRECISION (dst_type) < 32)
{
RESERVE (1);
/* Already converted to int, if needed. */
if (TYPE_PRECISION (dst_type) <= 8)
OP1 (OPCODE_i2b);
else if (TREE_UNSIGNED (dst_type))
OP1 (OPCODE_i2c);
else
OP1 (OPCODE_i2s);
}
}
NOTE_PUSH (1 + wide_dst);
}
}
break;
case TRY_EXPR:
{
tree try_clause = TREE_OPERAND (exp, 0);
tree finally = TREE_OPERAND (exp, 2);
struct jcf_block *start_label = get_jcf_label_here (state);
struct jcf_block *end_label; /* End of try clause. */
struct jcf_block *finally_label; /* Finally subroutine. */
struct jcf_block *finished_label = gen_jcf_label (state);
tree clause = TREE_OPERAND (exp, 1);
if (finally)
{
finally = FINALLY_EXPR_BLOCK (finally);
finally_label = gen_jcf_label (state);
}
if (target != IGNORE_TARGET)
abort ();
generate_bytecode_insns (try_clause, IGNORE_TARGET, state);
end_label = get_jcf_label_here (state);
if (CAN_COMPLETE_NORMALLY (try_clause))
emit_goto (finished_label, state);
for ( ; clause != NULL_TREE; clause = TREE_CHAIN (clause))
{
tree catch_clause = TREE_OPERAND (clause, 0);
tree exception_decl = BLOCK_EXPR_DECLS (catch_clause);
struct jcf_handler *handler = alloc_handler (start_label, end_label, state);
handler->type = TREE_TYPE (TREE_TYPE (exception_decl));
generate_bytecode_insns (catch_clause, IGNORE_TARGET, state);
if (CAN_COMPLETE_NORMALLY (catch_clause))
emit_goto (finished_label, state);
}
if (finally)
{
tree return_link;
tree exception_type = build_pointer_type (throwable_type_node);
tree exception_decl = build_decl (VAR_DECL, NULL_TREE,
exception_type);
struct jcf_handler *handler
= alloc_handler (start_label, NULL_TREE, state);
handler->end_label = handler->handler_label;
handler->type = NULL_TREE;
localvar_alloc (exception_decl, state);
NOTE_PUSH (1);
emit_store (exception_decl, state);
emit_jsr (finally_label, state);
emit_load (exception_decl, state);
RESERVE (1);
OP1 (OPCODE_athrow);
NOTE_POP (1);
localvar_free (exception_decl, state);
/* The finally block. */
return_link = build_decl (VAR_DECL, NULL_TREE,
return_address_type_node);
define_jcf_label (finally_label, state);
NOTE_PUSH (1);
localvar_alloc (return_link, state);
emit_store (return_link, state);
generate_bytecode_insns (finally, IGNORE_TARGET, state);
maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
localvar_free (return_link, state);
}
define_jcf_label (finished_label, state);
if (finally)
emit_jsr (finally_label, state);
}
break;
case THROW_EXPR:
generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state);
RESERVE (1);
OP1 (OPCODE_athrow);
break;
case NEW_CLASS_EXPR:
{
tree class = TREE_TYPE (TREE_TYPE (exp));
int index = find_class_constant (&state->cpool, class);
RESERVE (4);
OP1 (OPCODE_new);
OP2 (index);
OP1 (OPCODE_dup);
NOTE_PUSH (1);
}
/* ... fall though ... */
case CALL_EXPR: case CALL_EXPR:
{ {
tree t; tree f = TREE_OPERAND (exp, 0);
tree x = TREE_OPERAND (exp, 1);
int save_SP = state->code_SP; int save_SP = state->code_SP;
for (t = TREE_OPERAND (exp, 1); t != NULL_TREE; t = TREE_CHAIN (t)) if (TREE_CODE (f) == ADDR_EXPR)
f = TREE_OPERAND (f, 0);
if (f == soft_newarray_node)
{
int type_code = TREE_INT_CST_LOW (TREE_VALUE (x));
generate_bytecode_insns (TREE_VALUE (TREE_CHAIN (x)),
STACK_TARGET, state);
RESERVE (2);
OP1 (OPCODE_newarray);
OP1 (type_code);
break;
}
else if (f == soft_multianewarray_node)
{
int ndims;
int idim;
int index = find_class_constant (&state->cpool,
TREE_TYPE (TREE_TYPE (exp)));
x = TREE_CHAIN (x); /* Skip class argument. */
ndims = TREE_INT_CST_LOW (TREE_VALUE (x));
for (idim = ndims; --idim >= 0; )
{
x = TREE_CHAIN (x);
generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state);
}
RESERVE (4);
OP1 (OPCODE_multianewarray);
OP2 (index);
OP1 (ndims);
break;
}
else if (f == soft_anewarray_node)
{ {
generate_bytecode_insns (TREE_VALUE (t), STACK_TARGET, state); tree cl = TYPE_ARRAY_ELEMENT (TREE_TYPE (TREE_TYPE (exp)));
int index = find_class_constant (&state->cpool, TREE_TYPE (cl));
generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state);
RESERVE (3);
OP1 (OPCODE_anewarray);
OP2 (index);
break;
}
else if (exp == soft_exceptioninfo_call_node)
{
NOTE_PUSH (1); /* Pushed by exception system. */
break;
}
for ( ; x != NULL_TREE; x = TREE_CHAIN (x))
{
generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state);
} }
t = TREE_OPERAND (exp, 0);
state->code_SP = save_SP; state->code_SP = save_SP;
if (TREE_CODE (t) == FUNCTION_DECL) if (TREE_CODE (f) == FUNCTION_DECL && DECL_CONTEXT (f) != NULL_TREE)
{ {
int index = find_methodref_index (&state->cpool, t); int index = find_methodref_index (&state->cpool, f);
RESERVE (3); RESERVE (3);
if (DECL_CONSTRUCTOR_P (t)) if (DECL_CONSTRUCTOR_P (f))
OP1 (OPCODE_invokespecial); OP1 (OPCODE_invokespecial);
else if (METHOD_STATIC (t)) else if (METHOD_STATIC (f))
OP1 (OPCODE_invokestatic); OP1 (OPCODE_invokestatic);
else else
OP1 (OPCODE_invokevirtual); OP1 (OPCODE_invokevirtual);
OP2 (index); OP2 (index);
t = TREE_TYPE (TREE_TYPE (t)); f = TREE_TYPE (TREE_TYPE (f));
if (TREE_CODE (t) != VOID_TYPE) if (TREE_CODE (f) != VOID_TYPE)
{ {
int size = TYPE_IS_WIDE (t) ? 2 : 1; int size = TYPE_IS_WIDE (f) ? 2 : 1;
if (target == IGNORE_TARGET) if (target == IGNORE_TARGET)
emit_pop (size, state); emit_pop (size, state);
else else
...@@ -1476,7 +2060,13 @@ perform_relocations (state) ...@@ -1476,7 +2060,13 @@ perform_relocations (state)
int pc; int pc;
int shrink; int shrink;
/* Figure out the actual locations of each block. */ /* Before we start, the pc field of each block is an upper bound on
the block's start pc (it may be less, if previous blocks need less
than their maximum).
The minimum size of each block is in the block's chunk->size. */
/* First, figure out the actual locations of each block. */
pc = 0; pc = 0;
shrink = 0; shrink = 0;
for (block = state->blocks; block != NULL; block = block->next) for (block = state->blocks; block != NULL; block = block->next)
...@@ -1489,9 +2079,9 @@ perform_relocations (state) ...@@ -1489,9 +2079,9 @@ perform_relocations (state)
Assumes relocations are in reverse order. */ Assumes relocations are in reverse order. */
reloc = block->u.relocations; reloc = block->u.relocations;
while (reloc != NULL while (reloc != NULL
&& reloc->kind == OPCODE_goto_w
&& reloc->label->pc == block->next->pc && reloc->label->pc == block->next->pc
&& reloc->offset + 2 == block_size && reloc->offset + 2 == block_size)
&& reloc->kind == OPCODE_goto_w)
{ {
reloc = reloc->next; reloc = reloc->next;
block->u.relocations = reloc; block->u.relocations = reloc;
...@@ -1502,7 +2092,15 @@ perform_relocations (state) ...@@ -1502,7 +2092,15 @@ perform_relocations (state)
for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next)
{ {
if (reloc->kind < -1 || reloc->kind > 0) if (reloc->kind == SWITCH_ALIGN_RELOC)
{
/* We assume this is the first relocation in this block,
so we know its final pc. */
int where = pc + reloc->offset;
int pad = ((where + 3) & ~3) - where;
block_size += pad;
}
else if (reloc->kind < -1 || reloc->kind > BLOCK_START_RELOC)
{ {
int delta = reloc->label->pc - (pc + reloc->offset - 1); int delta = reloc->label->pc - (pc + reloc->offset - 1);
int expand = reloc->kind > 0 ? 2 : 5; int expand = reloc->kind > 0 ? 2 : 5;
...@@ -1536,16 +2134,26 @@ perform_relocations (state) ...@@ -1536,16 +2134,26 @@ perform_relocations (state)
{ {
chunk->data = (unsigned char *) chunk->data = (unsigned char *)
obstack_alloc (state->chunk_obstack, new_size); obstack_alloc (state->chunk_obstack, new_size);
chunk->size = new_size;
} }
new_ptr = chunk->data + new_size; new_ptr = chunk->data + new_size;
/* We do the relocations from back to front, because /* We do the relocations from back to front, because
thre relocations are in reverse order. */ the relocations are in reverse order. */
for (reloc = block->u.relocations; ; reloc = reloc->next) for (reloc = block->u.relocations; ; reloc = reloc->next)
{ {
/* Lower old index of piece to be copied with no relocation. */ /* new_ptr and old_ptr point into the old and new buffers,
respectively. (If no relocations cause the buffer to
grow, the buffer will be the same buffer, and new_ptr==old_ptr.)
The bytes at higher adress have been copied and relocations
handled; those at lower addresses remain to process. */
/* Lower old index of piece to be copied with no relocation.
I.e. high index of the first piece that does need relocation. */
int start = reloc == NULL ? 0 int start = reloc == NULL ? 0
: reloc->kind == 0 ? reloc->offset + 4 : reloc->kind == SWITCH_ALIGN_RELOC ? reloc->offset
: (reloc->kind == 0 || reloc->kind == BLOCK_START_RELOC)
? reloc->offset + 4
: reloc->offset + 2; : reloc->offset + 2;
int32 value; int32 value;
int new_offset; int new_offset;
...@@ -1553,21 +2161,36 @@ perform_relocations (state) ...@@ -1553,21 +2161,36 @@ perform_relocations (state)
new_ptr -= n; new_ptr -= n;
old_ptr -= n; old_ptr -= n;
if (n > 0) if (n > 0)
bcopy (old_ptr, new_ptr, n); memcpy (new_ptr, old_ptr, n);
if (old_ptr == old_buffer) if (old_ptr == old_buffer)
break; break;
new_offset = new_ptr - chunk->data;
new_offset -= (reloc->kind == -1 ? 2 : 4);
if (reloc->kind == 0) if (reloc->kind == 0)
{ {
old_ptr -= 4; old_ptr -= 4;
value = GET_u4 (old_ptr); value = GET_u4 (old_ptr);
} }
else if (reloc->kind == BLOCK_START_RELOC)
{
old_ptr -= 4;
value = 0;
new_offset = 0;
}
else if (reloc->kind == SWITCH_ALIGN_RELOC)
{
int where = block->pc + reloc->offset;
int pad = ((where + 3) & ~3) - where;
while (--pad >= 0)
*--new_ptr = 0;
continue;
}
else else
{ {
old_ptr -= 2; old_ptr -= 2;
value = GET_u2 (old_ptr); value = GET_u2 (old_ptr);
} }
new_offset = new_ptr - chunk->data - (reloc->kind == -1 ? 2 : 4);
value += reloc->label->pc - (block->pc + new_offset); value += reloc->label->pc - (block->pc + new_offset);
*--new_ptr = (unsigned char) value; value >>= 8; *--new_ptr = (unsigned char) value; value >>= 8;
*--new_ptr = (unsigned char) value; value >>= 8; *--new_ptr = (unsigned char) value; value >>= 8;
...@@ -1576,7 +2199,7 @@ perform_relocations (state) ...@@ -1576,7 +2199,7 @@ perform_relocations (state)
*--new_ptr = (unsigned char) value; value >>= 8; *--new_ptr = (unsigned char) value; value >>= 8;
*--new_ptr = (unsigned char) value; *--new_ptr = (unsigned char) value;
} }
if (reloc->kind > 0) if (reloc->kind > BLOCK_START_RELOC)
{ {
/* Convert: OP TARGET to: OP_w TARGET; (OP is goto or jsr). */ /* Convert: OP TARGET to: OP_w TARGET; (OP is goto or jsr). */
--old_ptr; --old_ptr;
...@@ -1624,6 +2247,9 @@ init_jcf_method (state, method) ...@@ -1624,6 +2247,9 @@ init_jcf_method (state, method)
BUFFER_RESET (&state->localvars); BUFFER_RESET (&state->localvars);
state->code_SP = 0; state->code_SP = 0;
state->code_SP_max = 0; state->code_SP_max = 0;
state->handlers = NULL;
state->last_handler = NULL;
state->num_handlers = 0;
} }
void void
...@@ -1731,6 +2357,7 @@ generate_classfile (clas, state) ...@@ -1731,6 +2357,7 @@ generate_classfile (clas, state)
static tree Code_node = NULL_TREE; static tree Code_node = NULL_TREE;
tree t; tree t;
char *attr_len_ptr; char *attr_len_ptr;
struct jcf_handler *handler;
if (Code_node == NULL_TREE) if (Code_node == NULL_TREE)
Code_node = get_identifier ("Code"); Code_node = get_identifier ("Code");
ptr = append_chunk (NULL, 14, state); ptr = append_chunk (NULL, 14, state);
...@@ -1741,13 +2368,20 @@ generate_classfile (clas, state) ...@@ -1741,13 +2368,20 @@ generate_classfile (clas, state)
for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t)) for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
localvar_alloc (t, state); localvar_alloc (t, state);
generate_bytecode_insns (body, IGNORE_TARGET, state); generate_bytecode_insns (body, IGNORE_TARGET, state);
if (CAN_COMPLETE_NORMALLY (body))
{
if (TREE_CODE (TREE_TYPE (type)) != VOID_TYPE)
abort();
RESERVE (1);
OP1 (OPCODE_return);
}
for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t)) for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
localvar_free (t, state); localvar_free (t, state);
finish_jcf_block (state); finish_jcf_block (state);
perform_relocations (state); perform_relocations (state);
ptr = attr_len_ptr; ptr = attr_len_ptr;
i = 8 + state->code_length + 4; i = 8 + state->code_length + 4 + 8 * state->num_handlers;
if (state->linenumber_count > 0) if (state->linenumber_count > 0)
{ {
code_attributes_count++; code_attributes_count++;
...@@ -1762,8 +2396,26 @@ generate_classfile (clas, state) ...@@ -1762,8 +2396,26 @@ generate_classfile (clas, state)
PUT2 (state->code_SP_max); /* max_stack */ PUT2 (state->code_SP_max); /* max_stack */
PUT2 (localvar_max); /* max_locals */ PUT2 (localvar_max); /* max_locals */
PUT4 (state->code_length); PUT4 (state->code_length);
ptr = append_chunk (NULL, 4, state);
PUT2 (0); /* exception_table_length */ /* Emit the exception table. */
ptr = append_chunk (NULL, 2 + 8 * state->num_handlers, state);
PUT2 (state->num_handlers); /* exception_table_length */
handler = state->handlers;
for (; handler != NULL; handler = handler->next)
{
int type_index;
PUT2 (handler->start_label->pc);
PUT2 (handler->end_label->pc);
PUT2 (handler->handler_label->pc);
if (handler->type == NULL_TREE)
type_index = 0;
else
type_index = find_class_constant (&state->cpool,
handler->type);
PUT2 (type_index);
}
ptr = append_chunk (NULL, 2, state);
PUT2 (code_attributes_count); PUT2 (code_attributes_count);
/* Write the LineNumberTable attribute. */ /* Write the LineNumberTable attribute. */
...@@ -1877,3 +2529,8 @@ write_classfile (clas) ...@@ -1877,3 +2529,8 @@ write_classfile (clas)
fatal ("failed to close after writing `%s'", class_file_name); fatal ("failed to close after writing `%s'", class_file_name);
release_jcf_state (state); release_jcf_state (state);
} }
/* TODO:
string concatenation
synchronized statement
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment