Commit e4de5a10 by Per Bothner

Merge from Cygnus internal source tree.

From-SVN: r23025
parent 8376a32e
......@@ -148,10 +148,12 @@ ALL_CFLAGS = $(INTERNAL_CFLAGS) $(X_CFLAGS) $(T_CFLAGS) $(CFLAGS) $(XCFLAGS)
# Likewise.
ALL_CPPFLAGS = $(CPPFLAGS) $(X_CPPFLAGS) $(T_CPPFLAGS)
# CYGNUS LOCAL: SUBDIR_USE_ALLOCA is different from FSF.
# Even if ALLOCA is set, don't use it if compiling with GCC.
SUBDIR_OBSTACK = `if [ x$(OBSTACK) != x ]; then echo ../$(OBSTACK); else true; fi`
SUBDIR_USE_ALLOCA = `case "${CC}" in "${OLDCC}") if [ x$(ALLOCA) != x ]; then echo ../$(ALLOCA); else true; fi ;; esac`
#SUBDIR_USE_ALLOCA = `case "${CC}" in "${OLDCC}") if [ x$(ALLOCA) != x ]; then echo ../$(ALLOCA); else true; fi ;; esac`
SUBDIR_USE_ALLOCA = `if [ x$(ALLOCA) != x ]; then echo ../$(ALLOCA); else true; fi`
SUBDIR_MALLOC = `if [ x$(MALLOC) != x ]; then echo ../$(MALLOC); else true; fi`
# How to link with both our special library facilities
......@@ -226,9 +228,10 @@ RTL_H = $(srcdir)/../rtl.h $(srcdir)/../rtl.def \
$(srcdir)/../machmode.h $(srcdir)/../machmode.def
EXPR_H = $(srcdir)/../expr.h ../insn-codes.h
# CYGNUS LOCAL: we put these files into the build dir.
PARSE_C = parse.c
PARSE_SCAN_C = parse-scan.c
PARSE_H = $(srcdir)/parse.h
PARSE_C = $(srcdir)/parse.c
PARSE_SCAN_C = $(srcdir)/parse-scan.c
$(PARSE_C): $(srcdir)/parse.y $(srcdir)/lex.c $(PARSE_H) $(srcdir)/lex.h
$(BISON) -t -v $(BISONFLAGS) $(JAVABISONFLAGS) -o $(PARSE_C) \
......@@ -239,6 +242,8 @@ $(PARSE_SCAN_C): $(srcdir)/parse-scan.y $(srcdir)/lex.c $(PARSE_H) \
lex.c: keyword.h lex.h
lang.o: $(srcdir)/java-tree.def
keyword.h: keyword.gperf
gperf -L KR-C -F ', 0' -p -t -j1 -i 1 -g -o -N java_keyword -k1,3,$$ \
keyword.gperf > keyword.h
......@@ -258,8 +263,9 @@ TAGS: force
mostlyclean:
rm -f *.o
# CYGNUS LOCAL: Remove these files, as they are in the build dir.
clean: mostlyclean
rm -f parse.c
rm -f parse.c parse-scan.c
force:
......
......@@ -36,6 +36,9 @@ struct buffer
#define NULL_BUFFER { (void*) 0, (void*) 0, (void*) 0 }
#define BUFFER_INIT(BUFP) \
((BUFP)->data = NULL, (BUFP)->ptr = NULL, (BUFP)->limit = NULL)
#define BUFFER_LENGTH(BUFP) ((BUFP)->ptr - (BUFP)->data)
#define BUFFER_RESET(BUFP) ((BUFP)->ptr = (BUFP)->data)
......
......@@ -161,6 +161,12 @@ method_init_exceptions ()
whole_range.first_child = NULL;
whole_range.next_sibling = NULL;
cache_range_start = 0xFFFFFF;
java_set_exception_lang_code ();
}
void
java_set_exception_lang_code ()
{
set_exception_lang_code (EH_LANG_Java);
set_exception_version_code (1);
}
......@@ -183,34 +189,44 @@ expand_start_java_handler (range)
expand_eh_region_start ();
}
/* if there are any handlers for this range, isssue end of range,
and then all handler blocks */
void
expand_end_java_handler (range)
struct eh_range *range;
tree
prepare_eh_table_type (type)
tree type;
{
tree handler = range->handlers;
expand_start_all_catch ();
for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler))
{
tree type = TREE_PURPOSE (handler);
tree exp;
/* The "type" (metch_info) in a (Java) exception table is one:
* a) NULL - meaning match any type in a try-finally.
* b) a pointer to a (ccmpiled) class (low-order bit 0).
* c) a pointer to the Utf8Const name of the class, plus one
* (which yields a value with low-order bit 1). */
push_obstacks (&permanent_obstack, &permanent_obstack);
if (type == NULL_TREE)
exp = null_pointer_node;
else if (is_compiled_class (type))
exp = build_class_ref (type);
else
exp = fold (build (PLUS_EXPR, ptr_type_node,
exp = fold (build
(PLUS_EXPR, ptr_type_node,
build_utf8_ref (build_internal_class_name (type)),
size_one_node));
pop_obstacks ();
start_catch_handler (exp);
return exp;
}
/* if there are any handlers for this range, isssue end of range,
and then all handler blocks */
void
expand_end_java_handler (range)
struct eh_range *range;
{
tree handler = range->handlers;
expand_start_all_catch ();
for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler))
{
start_catch_handler (prepare_eh_table_type (TREE_PURPOSE (handler)));
/* Push the thrown object on the top of the stack */
expand_goto (TREE_VALUE (handler));
}
expand_end_all_catch ();
......
......@@ -460,7 +460,7 @@ java_stack_dup (size, offset)
}
}
/* Calls soft_athrow. Discard the contents of the value stack. */
/* Calls _Jv_Throw. Discard the contents of the value stack. */
tree
build_java_athrow (node)
......@@ -526,15 +526,16 @@ decode_newarray_type (int atype)
}
}
/* Build a call to soft_badarrayindex(), the ArrayIndexOfBoundsException
exception handler. */
/* Build a call to _Jv_ThrowBadArrayIndex(), the
ArrayIndexOfBoundsException exception handler. */
static tree
build_java_throw_out_of_bounds_exception ()
build_java_throw_out_of_bounds_exception (index)
tree index;
{
tree node = build (CALL_EXPR, int_type_node,
build_address_of (soft_badarrayindex_node),
NULL_TREE, NULL_TREE );
build_tree_list (NULL_TREE, index), NULL_TREE);
TREE_SIDE_EFFECTS (node) = 1; /* Allows expansion within ANDIF */
return (node);
}
......@@ -629,7 +630,7 @@ build_java_arrayaccess (array, type, index)
if (! integer_zerop (test))
{
throw = build (TRUTH_ANDIF_EXPR, int_type_node, test,
build_java_throw_out_of_bounds_exception ());
build_java_throw_out_of_bounds_exception (index));
/* allows expansion within COMPOUND */
TREE_SIDE_EFFECTS( throw ) = 1;
}
......@@ -677,7 +678,7 @@ build_java_check_indexed_type (array_node, indexed_type)
return indexed_type;
}
/* newarray triggers a call to soft_newarray. This function should be called
/* newarray triggers a call to _Jv_NewArray. This function should be called
with an integer code (the type of array to create) and get from the stack
the size of the dimmension. */
......@@ -706,7 +707,7 @@ build_anewarray (class_type, length)
tree class_type;
tree length;
{
tree type = build_java_array_type (promote_type (class_type),
tree type = build_java_array_type (class_type,
TREE_CODE (length) == INTEGER_CST
? TREE_INT_CST_LOW (length)
: -1);
......@@ -719,9 +720,9 @@ build_anewarray (class_type, length)
NULL_TREE);
}
/* Generates a call to multianewarray. multianewarray expects a class pointer,
a number of dimensions and the matching number of dimensions. The argument
list is NULL terminated. */
/* Generates a call to _Jv_NewMultiArray. multianewarray expects a
class pointer, a number of dimensions and the matching number of
dimensions. The argument list is NULL terminated. */
void
expand_java_multianewarray (class_type, ndim)
......@@ -829,8 +830,8 @@ expand_java_array_length ()
push_value (build_java_arraynull_check (array, length, int_type_node));
}
/* Emit code for the call to soft_monitor{enter,exit}. CALL can be either
soft_monitorenter_node or soft_monitorexit_node. */
/* Emit code for the call to _Jv_Monitor{Enter,Exit}. CALL can be
either soft_monitorenter_node or soft_monitorexit_node. */
tree
build_java_monitor (call, object)
......@@ -1147,6 +1148,18 @@ lookup_label (pc)
}
}
/* Generate a unique name for the purpose of loops and switches
labels, and try-catch-finally blocks label or temporary variables. */
tree
generate_name ()
{
static int l_number = 0;
char buff [20];
sprintf (buff, "$L%d", l_number++);
return get_identifier (buff);
}
tree
create_label_decl (name)
tree name;
......@@ -1175,7 +1188,6 @@ note_label (current_pc, target_pc)
/* Emit code to jump to TARGET_PC if VALUE1 CONDITION VALUE2,
where CONDITION is one of one the compare operators. */
void
expand_compare (condition, value1, value2, target_pc)
enum tree_code condition;
......@@ -1279,7 +1291,14 @@ pop_arguments (arg_types)
if (TREE_CODE (arg_types) == TREE_LIST)
{
tree tail = pop_arguments (TREE_CHAIN (arg_types));
return tree_cons (NULL_TREE, pop_value (TREE_VALUE (arg_types)), tail);
tree type = TREE_VALUE (arg_types);
tree arg = pop_value (type);
#ifdef PROMOTE_PROTOTYPES
if (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)
&& INTEGRAL_TYPE_P (type))
arg = convert (integer_type_node, arg);
#endif
return tree_cons (NULL_TREE, arg, tail);
}
abort ();
}
......@@ -1490,17 +1509,6 @@ expand_invoke (opcode, method_ref_index, nargs)
arg_list = pop_arguments (TYPE_ARG_TYPES (method_type));
flush_quick_stack ();
if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial
&& ! inherits_from_p (current_class, self_type))
{ /* FIXME probably not needed for invokespecial if done by NEW. */
/* Ensure self_type is initialized. */
func = build (CALL_EXPR, void_type_node, soft_initclass_node,
build_tree_list (NULL_TREE,
build_class_ref (self_type)),
NULL_TREE);
expand_expr_stmt (func);
}
func = NULL_TREE;
if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial
|| (opcode == OPCODE_invokevirtual
......@@ -1515,9 +1523,9 @@ expand_invoke (opcode, method_ref_index, nargs)
func = build_invokevirtual (dtable, method);
else
{
/* We expand invokeinterface here. soft_lookupinterfacemethod () will
ensure that the selected method exists, is public and not abstract
nor static. */
/* We expand invokeinterface here.
_Jv_LookupInterfaceMethod() will ensure that the selected
method exists, is public and not abstract nor static. */
tree lookup_arg;
......@@ -1543,12 +1551,6 @@ expand_invoke (opcode, method_ref_index, nargs)
call = build (CALL_EXPR, TREE_TYPE (method_type), func, arg_list, NULL_TREE);
TREE_SIDE_EFFECTS (call) = 1;
if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial)
{ /* FIXME probably not needed for invokespecial if done by NEW. */
/* Ensure self_type is initialized. */
call = build_class_init (self_type, call);
}
if (TREE_CODE (TREE_TYPE (method_type)) == VOID_TYPE)
expand_expr_stmt (call);
else
......@@ -1600,7 +1602,7 @@ expand_java_field_op (is_static, is_putting, field_ref_index)
if (is_error)
{
if (! is_putting)
push_value (convert (promote_type (field_type), integer_zero_node));
push_value (convert (field_type, integer_zero_node));
flush_quick_stack ();
return;
}
......@@ -1610,7 +1612,7 @@ expand_java_field_op (is_static, is_putting, field_ref_index)
this is also needed to avoid circularities in the implementation
of these fields in libjava. */
if (field_name == TYPE_identifier_node && ! is_putting
&& field_type == class_type_node
&& field_type == class_ptr_type
&& strncmp (self_name, "java.lang.", 10) == 0)
{
char *class_name = self_name+10;
......@@ -1693,6 +1695,8 @@ java_lang_expand_expr (exp, target, tmode, modifier)
tree type = TREE_TYPE (exp);
register enum machine_mode mode = TYPE_MODE (type);
int unsignedp = TREE_UNSIGNED (type);
tree node, current;
int has_finally_p;
switch (TREE_CODE (exp))
{
......@@ -1719,6 +1723,61 @@ java_lang_expand_expr (exp, target, tmode, modifier)
}
break;
case SWITCH_EXPR:
java_expand_switch (exp);
return const0_rtx;
case TRY_EXPR:
/* We expand a try[-catch][-finally] block */
/* Expand the try block */
expand_eh_region_start ();
expand_expr_stmt (TREE_OPERAND (exp, 0));
expand_start_all_catch ();
has_finally_p = (TREE_OPERAND (exp, 2) ? 1 : 0);
/* Expand all catch clauses (EH handlers) */
for (current = TREE_OPERAND (exp, 1); current;
current = TREE_CHAIN (current))
{
extern rtx return_label;
tree type;
/* If we have a finally, the last exception handler is the
one that is supposed to catch everything. */
if (has_finally_p && !TREE_CHAIN (current))
type = NULL_TREE;
else
{
tree catch = java_get_catch_block (current, has_finally_p);
tree decl = BLOCK_EXPR_DECLS (catch);
type = TREE_TYPE (TREE_TYPE (decl));
}
start_catch_handler (prepare_eh_table_type (type));
expand_expr_stmt (TREE_OPERAND (current, 0));
/* Need to expand a goto to the end of the function here,
but not for the catch everything handler. */
if (type)
{
if (return_label)
emit_jump (return_label);
else
fatal ("No return_label for this function - "
"java_lang_expand_expr");
}
end_catch_handler ();
}
/* Expand the finally block, if any */
if (has_finally_p)
{
tree finally = TREE_OPERAND (exp, 2);
emit_label (label_rtx (FINALLY_EXPR_LABEL (finally)));
expand_expr_stmt (FINALLY_EXPR_BLOCK (finally));
}
expand_end_all_catch ();
break;
default:
fatal ("Can't expand '%s' tree - java_lang_expand_expr",
tree_code_name [TREE_CODE (exp)]);
......@@ -1984,6 +2043,15 @@ process_jvm_instruction (PC, byte_ops, length)
{
char *opname; /* Temporary ??? */
int oldpc = PC; /* PC at instruction start. */
/* If the instruction is at the beginning of a exception handler,
replace the top of the stack with the thrown object reference */
if (instruction_bits [PC] & BCODE_EXCEPTION_TARGET)
{
pop_value (ptr_type_node);
push_value (soft_exceptioninfo_call_node);
}
switch (byte_ops[PC++])
{
#define JAVAOP(OPNAME, OPCODE, OPKIND, OPERAND_TYPE, OPERAND_VALUE) \
......
......@@ -84,11 +84,21 @@ static JCF_u2 last_access;
#define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)
int seen_fields = 0;
/* We keep a linked list of all method names we have seen. This lets
us determine if a method name and a field name are in conflict. */
struct method_name
{
unsigned char *name;
int length;
struct method_name *next;
};
/* List of method names we've seen. */
static struct method_name *method_name_list;
static void print_field_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
static void print_method_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
static void print_c_decl PROTO ((FILE*, JCF*, int, int, JCF_u2, int));
static void print_c_decl PROTO ((FILE*, JCF*, int, int, JCF_u2, int, char *));
JCF_u2 current_field_name;
JCF_u2 current_field_value;
......@@ -99,8 +109,14 @@ JCF_u2 current_field_flags;
( current_field_name = (NAME), current_field_signature = (SIGNATURE), \
current_field_flags = (ACCESS_FLAGS), current_field_value = 0)
/* We pass over fields twice. The first time we just note the start
of the methods. Then we go back and parse the fields for real.
This is ugly. */
static int field_pass;
#define HANDLE_END_FIELD() \
print_field_info (out, jcf, current_field_name, current_field_signature, \
if (field_pass) print_field_info (out, jcf, current_field_name, \
current_field_signature, \
current_field_flags);
#define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX)
......@@ -242,11 +258,29 @@ generate_access (stream, flags)
}
}
/* See if NAME is already the name of a method. */
static int
name_is_method_p (name, length)
unsigned char *name;
int length;
{
struct method_name *p;
for (p = method_name_list; p != NULL; p = p->next)
{
if (p->length == length && ! memcmp (p->name, name, length))
return 1;
}
return 0;
}
static void
DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
FILE *stream AND JCF* jcf
AND int name_index AND int sig_index AND JCF_u2 flags)
{
char *override = NULL;
if (flags & ACC_FINAL)
{
if (current_field_value > 0)
......@@ -305,12 +339,42 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
generate_access (stream, flags);
fputs (" ", out);
if (flags & ACC_STATIC)
if ((flags & ACC_STATIC))
fputs ("static ", out);
print_c_decl (out, jcf, name_index, sig_index, flags, 0);
if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
}
else
{
unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
int length = JPOOL_UTF_LENGTH (jcf, name_index);
if (name_is_method_p (name, length))
{
/* This field name matches a method. So override the name
with a dummy name. This is yucky, but it isn't clear
what else to do. FIXME: if the field is static, then
we'll be in real trouble. */
if ((flags & ACC_STATIC))
{
fprintf (stderr, "static field has same name as method\n");
found_error = 1;
}
override = (char *) malloc (length + 3);
memcpy (override, name, length);
strcpy (override + length, "__");
}
}
print_c_decl (out, jcf, name_index, sig_index, flags, 0, override);
fputs (";\n", out);
if (! (flags & ACC_STATIC))
seen_fields++;
if (override)
free (override);
}
static void
......@@ -320,6 +384,7 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
{
unsigned char *str;
int length, is_init = 0;
char *override = NULL;
if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
fprintf (stream, "<not a UTF8 constant>");
......@@ -334,13 +399,33 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
else
return;
}
else
{
struct method_name *nn;
nn = (struct method_name *) malloc (sizeof (struct method_name));
nn->name = (char *) malloc (length);
memcpy (nn->name, str, length);
nn->length = length;
nn->next = method_name_list;
method_name_list = nn;
}
/* We can't generate a method whose name is a C++ reserved word.
For now the only problem has been `delete'; add more here as
required. FIXME: we need a better solution than just ignoring
the method. */
required. We can't just ignore the function, because that will
cause incorrect code to be generated if the function is virtual
(not only for calls to this function for for other functions
after it in the vtbl). So we give it a dummy name instead. */
if (! utf8_cmp (str, length, "delete"))
{
/* If the method is static, we can safely skip it. If we don't
skip it then we'll have problems since the mangling will be
wrong. FIXME. */
if ((flags & ACC_STATIC))
return;
override = "__dummy_delete";
}
generate_access (stream, flags);
......@@ -353,7 +438,7 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
if (! is_init)
fputs ("virtual ", out);
}
print_c_decl (out, jcf, name_index, sig_index, flags, is_init);
print_c_decl (out, jcf, name_index, sig_index, flags, is_init, override);
/* FIXME: it would be nice to decompile small methods here. That
would allow for inlining. */
......@@ -361,58 +446,24 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
fprintf(out, ";\n");
}
static void
DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init),
FILE* stream AND JCF* jcf
AND int name_index AND int signature_index AND JCF_u2 flags
AND int is_init)
/* Print one piece of a signature. Returns pointer to next parseable
character on success, NULL on error. */
static unsigned char *
decode_signature_piece (stream, signature, limit, need_space)
FILE *stream;
unsigned char *signature, *limit;
int *need_space;
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
fprintf (stream, "<not a UTF8 constant>");
else
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register unsigned char *str = str0;
unsigned char *limit = str + length;
int j;
char *ctype;
int need_space = 0;
int is_method = str[0] == '(';
if (is_method)
{
/* Skip to the return signature, and print that first.
However, don't do this is we are printing a construtcor.
*/
if (is_init)
{
str = str0 + 1;
/* FIXME: Most programmers love Celtic knots because
they see their own code in the interconnected loops.
That is, this is spaghetti. */
goto have_constructor;
}
else
{
while (str < limit)
{
int ch = *str++;
if (ch == ')')
break;
}
}
}
again:
while (str < limit)
{
switch (str[0])
switch (signature[0])
{
case '[':
for (str++; str < limit && *str >= '0' && *str <= '9'; str++)
for (signature++; (signature < limit
&& *signature >= '0'
&& *signature <= '9'); signature++)
;
switch (*str)
switch (*signature)
{
case 'B': ctype = "jbyteArray"; goto printit;
case 'C': ctype = "jcharArray"; goto printit;
......@@ -427,33 +478,31 @@ DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init),
/* We have to generate a reference to JArray here,
so that our output matches what the compiler
does. */
++str;
fputs ("JArray<", out);
while (str < limit && *str != ';')
++signature;
fputs ("JArray<", stream);
while (signature < limit && *signature != ';')
{
int ch = UTF8_GET (str, limit);
int ch = UTF8_GET (signature, limit);
if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
}
fputs (" *> *", out);
need_space = 0;
++str;
fputs (" *> *", stream);
*need_space = 0;
++signature;
break;
default:
fprintf (stderr, "unparseable signature `%s'\n", str0);
found_error = 1;
ctype = "???"; goto printit;
/* Unparseable signature. */
return NULL;
}
break;
case '(':
fputc (*str++, stream);
continue;
case ')':
fputc (*str++, stream);
/* the return signature was printed in the first pass. */
return;
/* This shouldn't happen. */
return NULL;
case 'B': ctype = "jbyte"; goto printit;
case 'C': ctype = "jchar"; goto printit;
case 'D': ctype = "jdouble"; goto printit;
......@@ -464,51 +513,118 @@ DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init),
case 'Z': ctype = "jboolean"; goto printit;
case 'V': ctype = "void"; goto printit;
case 'L':
++str;
while (*str && *str != ';')
++signature;
while (*signature && *signature != ';')
{
int ch = UTF8_GET (str, limit);
int ch = UTF8_GET (signature, limit);
if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
}
fputs (" *", stream);
if (*str == ';')
str++;
need_space = 0;
if (*signature == ';')
signature++;
*need_space = 0;
break;
default:
need_space = 1;
jcf_print_char (stream, *str++);
*need_space = 1;
jcf_print_char (stream, *signature++);
break;
printit:
str++;
need_space = 1;
signature++;
*need_space = 1;
fputs (ctype, stream);
break;
}
if (is_method && str < limit && *str != ')')
fputs (", ", stream);
return signature;
}
static void
DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init,
name_override),
FILE* stream AND JCF* jcf
AND int name_index AND int signature_index AND JCF_u2 flags
AND int is_init AND char *name_override)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
}
else
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register unsigned char *str = str0;
unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
unsigned char *next;
/* If printing a method, skip to the return signature and print
that first. However, there is no return value if this is a
constructor. */
if (is_method && ! is_init)
{
while (str < limit)
{
int ch = *str++;
if (ch == ')')
break;
}
}
have_constructor:
if (name_index)
/* If printing a field or an ordinary method, then print the
"return value" now. */
if (! is_method || ! is_init)
{
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
fprintf (stderr, "unparseable signature: `%s'\n", str0);
found_error = 1;
return;
}
}
/* Now print the name of the thing. */
if (need_space)
fprintf (stream, " ");
fputs (" ", stream);
if (name_override)
fputs (name_override, stream);
else if (name_index)
{
/* Declare constructors specially. */
if (is_init)
print_base_classname (stream, jcf, jcf->this_class);
else
print_name (stream, jcf, name_index);
}
if (is_method)
{
/* Have a method or a constructor. Print signature pieces
until done. */
fputs (" (", stream);
/* Go to beginning, skipping '('. */
str = str0 + 1;
goto again; /* To handle argument signatures. */
while (str < limit && *str != ')')
{
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
fprintf (stderr, "unparseable signature: `%s'\n", str0);
found_error = 1;
return;
}
if (next < limit && *next != ')')
fputs (", ", stream);
str = next;
}
fputs (")", stream);
}
}
}
......@@ -613,6 +729,7 @@ DEFUN(process_file, (jcf, out),
JCF *jcf AND FILE *out)
{
int code, i;
uint32 field_start, method_end;
current_jcf = main_jcf = jcf;
......@@ -700,8 +817,22 @@ DEFUN(process_file, (jcf, out),
as we see them. We have to list the methods in the same order
that they appear in the class file, so that the Java and C++
vtables have the same layout. */
/* We want to parse the methods first. But we need to find where
they start. So first we skip the fields, then parse the
methods. Then we parse the fields and skip the methods. FIXME:
this is ugly. */
field_pass = 0;
field_start = JCF_TELL (jcf);
jcf_parse_fields (jcf);
jcf_parse_methods (jcf);
method_end = JCF_TELL (jcf);
field_pass = 1;
JCF_SEEK (jcf, field_start);
jcf_parse_fields (jcf);
JCF_SEEK (jcf, method_end);
jcf_parse_final_attributes (jcf);
/* Generate friend decl if we still must. */
......
......@@ -408,7 +408,7 @@ get_class_constant (JCF *jcf , int i)
char *name = JPOOL_UTF_DATA (jcf, name_index);
int nlength = JPOOL_UTF_LENGTH (jcf, name_index);
if (name[0] == '[') /* Handle array "classes". */
type = parse_signature_string (name, nlength);
type = TREE_TYPE (parse_signature_string (name, nlength));
else
{
tree cname = unmangle_classname (name, nlength);
......
......@@ -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. */
/* Append a line number entry for the given PC and LINE into
linenumbers.data. This will later before a LineNumberTable attribute. */
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
put_linenumber (pc, line)
int pc, line;
{
register unsigned char *ptr;
if (linenumbers.ptr == linenumbers.limit)
buffer_grow (&linenumbers, 4);
ptr = linenumbers.ptr;
PUT2 (pc);
PUT2 (line);
linenumbers.ptr = ptr;
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;
}
}
/* Note a line number entry for the current PC and given LINE. */
void
put_linenumber (line, state)
int line;
struct jcf_partial *state;
{
(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
info->debug_offset = -1;
state->first_lvar = info;
state->last_lvar = info;
state->lvar_count++;
}
}
int
localvar_free (decl, end_pc)
localvar_free (decl, state)
tree decl;
int end_pc;
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,8 +1121,52 @@ 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:
......@@ -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)))
{
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);
if (TREE_CODE (body) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (body, 0)) == EXIT_EXPR)
{
/* 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);
struct jcf_block *head_label = get_jcf_label_here (state);
generate_bytecode_insns (body, IGNORE_TARGET, state);
emit_goto (head_label, state);
}
generate_bytecode_insns (method, rhs, STACK_TARGET);
}
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)
{
RESERVE (1);
OP1 (TYPE_IS_WIDE (type) ? OPCODE_dup2_x1 : OPCODE_dup_x1);
if (! post_op)
emit_load (exp, state);
NOTE_PUSH (1);
}
if (TREE_CODE (lhs) == COMPONENT_REF)
break;
}
if (TREE_CODE (exp) == COMPONENT_REF)
{
tree field = TREE_OPERAND (lhs, 1);
field_op (field,
FIELD_STATIC (field) ? OPCODE_putstatic
: OPCODE_putfield);
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 (lhs) == VAR_DECL
|| TREE_CODE (lhs) == PARM_DECL)
else if (TREE_CODE (exp) == ARRAY_REF)
{
if (FIELD_STATIC (lhs))
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)
{
field_op (lhs, OPCODE_putstatic);
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:
{
int index = DECL_LOCAL_INDEX (lhs);
int opcode = adjust_typed_op (TREE_TYPE (lhs));
if (index <= 3)
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))
{
RESERVE (1);
opcode = 59 + 4 * opcode + index;
OP1 (opcode); /* [ilfda]store_[0123] */
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)
{
min_value++;
max_value++;
}
else
else if (arg1 == lhs)
{
maybe_wide (54 + opcode, index); /* [ilfda]store */
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))
{
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));
generate_bytecode_insns (TREE_OPERAND(lhs, 0), STACK_TARGET, state);
generate_bytecode_insns (TREE_OPERAND(lhs, 1), STACK_TARGET, state);
}
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);
}
......@@ -32,6 +32,40 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */
#include "jcf.h"
#include "toplev.h"
/* Table indexed by tree code giving a string containing a character
classifying the tree code. Possibilities are
t, d, s, c, r, <, 1 and 2. See java/java-tree.def for details. */
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
char java_tree_code_type[] = {
'x',
#include "java-tree.def"
};
#undef DEFTREECODE
/* Table indexed by tree code giving number of expression
operands beyond the fixed part of the node structure.
Not used for types or decls. */
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,
int java_tree_code_length[] = {
0,
#include "java-tree.def"
};
#undef DEFTREECODE
/* Names of tree components.
Used for printing out the tree and error messages. */
#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
char *java_tree_code_name[] = {
"@@dummy",
#include "java-tree.def"
};
#undef DEFTREECODE
int compiling_from_source;
char *language_string = "GNU Java";
......@@ -320,6 +354,20 @@ lang_init ()
current_jcf = main_jcf;
flag_exceptions = 1;
/* Append to Gcc tree node definition arrays */
bcopy (java_tree_code_type,
tree_code_type + (int) LAST_AND_UNUSED_TREE_CODE,
(int)LAST_JAVA_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE);
bcopy ((char *)java_tree_code_length,
(char *)(tree_code_length + (int) LAST_AND_UNUSED_TREE_CODE),
(LAST_JAVA_TREE_CODE -
(int)LAST_AND_UNUSED_TREE_CODE) * sizeof (int));
bcopy ((char *)java_tree_code_name,
(char *)(tree_code_name + (int) LAST_AND_UNUSED_TREE_CODE),
(LAST_JAVA_TREE_CODE -
(int)LAST_AND_UNUSED_TREE_CODE) * sizeof (char *));
}
/* This doesn't do anything on purpose. It's used to satisfy the
......
......@@ -148,22 +148,32 @@ extern tree stabilize_reference PROTO ((tree));
EXPR_WFL_EMIT_LINE_NOTE (node) = 1, node : node)
/* Types classification, according to the JLS, section 4.2 */
#define JFLOAT_TYPE_P(TYPE) (TREE_CODE ((TYPE)) == REAL_TYPE)
#define JINTEGRAL_TYPE_P(TYPE) ((TREE_CODE ((TYPE)) == INTEGER_TYPE) \
|| (TREE_CODE ((TYPE)) == CHAR_TYPE))
#define JNUMERIC_TYPE_P(TYPE) (JFLOAT_TYPE_P ((TYPE)) \
|| JINTEGRAL_TYPE_P ((TYPE)))
#define JPRIMITIVE_TYPE_P(TYPE) (JNUMERIC_TYPE_P ((TYPE)) \
|| (TREE_CODE ((TYPE)) == BOOLEAN_TYPE))
#define JFLOAT_TYPE_P(TYPE) (TYPE && TREE_CODE ((TYPE)) == REAL_TYPE)
#define JINTEGRAL_TYPE_P(TYPE) ((TYPE) \
&& (TREE_CODE ((TYPE)) == INTEGER_TYPE \
|| TREE_CODE ((TYPE)) == CHAR_TYPE))
#define JNUMERIC_TYPE_P(TYPE) ((TYPE) \
&& (JFLOAT_TYPE_P ((TYPE)) \
|| JINTEGRAL_TYPE_P ((TYPE))))
#define JPRIMITIVE_TYPE_P(TYPE) ((TYPE) \
&& (JNUMERIC_TYPE_P ((TYPE)) \
|| TREE_CODE ((TYPE)) == BOOLEAN_TYPE))
/* Not defined in the LRM */
#define JSTRING_TYPE_P(TYPE) ((TYPE) == string_type_node || \
#define JSTRING_TYPE_P(TYPE) ((TYPE) \
&& ((TYPE) == string_type_node || \
(TREE_CODE (TYPE) == POINTER_TYPE && \
TREE_TYPE (op1_type) == string_type_node))
#define JREFERENCE_TYPE_P(TYPE) (TREE_CODE (TYPE) == RECORD_TYPE || \
(TREE_CODE (TYPE) == POINTER_TYPE && \
TREE_CODE (TREE_TYPE (TYPE)) == RECORD_TYPE))
TREE_TYPE (TYPE) == string_type_node)))
#define JSTRING_P(NODE) ((NODE) \
&& (TREE_CODE (NODE) == STRING_CST \
|| IS_CRAFTED_STRING_BUFFER_P (NODE) \
|| JSTRING_TYPE_P (TREE_TYPE (NODE))))
#define JREFERENCE_TYPE_P(TYPE) ((TYPE) \
&& (TREE_CODE (TYPE) == RECORD_TYPE \
|| (TREE_CODE (TYPE) == POINTER_TYPE \
&& TREE_CODE (TREE_TYPE (TYPE)) == \
RECORD_TYPE)))
/* Other predicate */
#define DECL_P(NODE) (NODE && (TREE_CODE (NODE) == PARM_DECL \
......@@ -201,9 +211,9 @@ extern tree stabilize_reference PROTO ((tree));
((WFL), "Variable `%s' may not have been initialized", \
IDENTIFIER_POINTER (V))
/* Definition for loop handling. This Java's own definition of a loop
body. See parse.y for documentation. It's valid once you hold a
loop's body (LOOP_EXPR_BODY) */
/* Definition for loop handling. This is Java's own definition of a
loop body. See parse.y for documentation. It's valid once you hold
a loop's body (LOOP_EXPR_BODY) */
/* The loop main block is the one hold the condition and the loop body */
#define LOOP_EXPR_BODY_MAIN_BLOCK(NODE) TREE_OPERAND (NODE, 0)
......@@ -252,7 +262,6 @@ extern tree stabilize_reference PROTO ((tree));
}
#define POP_LOOP() ctxp->current_loop = TREE_CHAIN (ctxp->current_loop)
/* Invocation modes, as returned by invocation_mode (). */
enum {
INVOKE_STATIC,
......@@ -414,6 +423,14 @@ static jdeplist *reverse_jdep_list ();
#define COMPLETE_CHECK_OP_0(NODE) COMPLETE_CHECK_OP(NODE, 0)
#define COMPLETE_CHECK_OP_1(NODE) COMPLETE_CHECK_OP(NODE, 1)
/* Building invocations: append(ARG) and StringBuffer(ARG) */
#define BUILD_APPEND(ARG) \
build_method_invocation (wfl_append, \
(ARG ? build_tree_list (NULL, (ARG)): NULL_TREE))
#define BUILD_STRING_BUFFER(ARG) \
build_new_invocation (wfl_string_buffer, \
(ARG ? build_tree_list (NULL, (ARG)) : NULL_TREE))
/* Parser context data structure. */
struct parser_ctxt {
......@@ -472,7 +489,8 @@ struct parser_ctxt {
#ifndef JC1_LITE
static char *java_accstring_lookup PROTO ((int));
static void parse_error PROTO ((char *));
static void redefinition_error PROTO ((char *,tree, tree, tree));
static void classitf_redefinition_error PROTO ((char *,tree, tree, tree));
static void variable_redefinition_error PROTO ((tree, tree, tree, int));
static void check_modifiers PROTO ((char *, int, int));
static tree create_class PROTO ((int, tree, tree, tree));
static tree create_interface PROTO ((int, tree, tree));
......@@ -490,6 +508,7 @@ static tree method_header PROTO ((int, tree, tree, tree));
static tree method_declarator PROTO ((tree, tree));
static void parse_error_context VPROTO ((tree cl, char *msg, ...));
static void parse_warning_context VPROTO ((tree cl, char *msg, ...));
static tree parse_jdk1_1_error PROTO ((char *));
static void complete_class_report_errors PROTO ((jdep *));
static int process_imports PROTO ((void));
static void read_import_dir PROTO ((tree));
......@@ -514,7 +533,9 @@ static tree resolve_and_layout PROTO ((tree, tree));
static tree resolve_no_layout PROTO ((tree, tree));
static int identical_subpath_p PROTO ((tree, tree));
static int invocation_mode PROTO ((tree, int));
static tree refine_accessible_methods_list PROTO ((int, tree));
static tree find_applicable_accessible_methods_list PROTO ((tree, tree, tree));
static tree find_most_specific_methods_list PROTO ((tree));
static int argument_types_convertible PROTO ((tree, tree));
static tree patch_invoke PROTO ((tree, tree, tree, tree));
static tree lookup_method_invoke PROTO ((int, tree, tree, tree, tree));
static tree register_incomplete_type PROTO ((int, tree, tree, tree));
......@@ -525,10 +546,12 @@ static int unresolved_type_p PROTO ((tree, tree *));
static void create_jdep_list PROTO ((struct parser_ctxt *));
static tree build_expr_block PROTO ((tree, tree));
static tree enter_block PROTO ((void));
static tree enter_a_block PROTO ((tree));
static tree exit_block PROTO ((void));
static tree lookup_name_in_blocks PROTO ((tree));
static void maybe_absorb_scoping_blocks PROTO ((void));
static tree build_method_invocation PROTO ((tree, tree));
static tree build_new_invocation PROTO ((tree, tree));
static tree build_assignment PROTO ((int, int, tree, tree));
static tree build_binop PROTO ((enum tree_code, int, tree, tree));
static tree patch_assignment PROTO ((tree, tree, tree ));
......@@ -539,7 +562,11 @@ static tree patch_unaryop PROTO ((tree, tree));
static tree build_cast PROTO ((int, tree, tree));
static tree patch_cast PROTO ((tree, tree, tree));
static int valid_ref_assignconv_cast_p PROTO ((tree, tree, int));
static int can_cast_to_p PROTO ((tree, tree));
static int valid_builtin_assignconv_identity_widening_p PROTO ((tree, tree));
static int valid_cast_to_p PROTO ((tree, tree));
static int valid_method_invocation_conversion_p PROTO ((tree, tree));
static tree try_builtin_assignconv PROTO ((tree, tree, tree));
static tree try_reference_assignconv PROTO ((tree, tree));
static tree build_unresolved_array_type PROTO ((tree));
static tree build_array_ref PROTO ((int, tree, tree));
static tree patch_array_ref PROTO ((tree, tree, tree));
......@@ -565,6 +592,7 @@ static int class_in_current_package PROTO ((tree));
static tree build_if_else_statement PROTO ((int, tree, tree, tree));
static tree patch_if_else_statement PROTO ((tree));
static tree add_stmt_to_compound PROTO ((tree, tree, tree));
static tree add_stmt_to_block PROTO ((tree, tree, tree));
static tree patch_exit_expr PROTO ((tree));
static tree build_labeled_block PROTO ((int, tree, tree));
static tree generate_labeled_block PROTO (());
......@@ -577,6 +605,14 @@ static tree build_loop_body PROTO ((int, tree, int));
static tree complete_loop_body PROTO ((int, tree, tree, int));
static tree build_debugable_stmt PROTO ((int, tree));
static tree complete_for_loop PROTO ((int, tree, tree, tree));
static tree patch_switch_statement PROTO ((tree));
static tree string_constant_concatenation PROTO ((tree, tree));
static tree build_string_concatenation PROTO ((tree, tree));
static tree patch_string_cst PROTO ((tree));
static tree patch_string PROTO ((tree));
static tree build_jump_to_finally PROTO ((tree, tree, tree, tree));
static tree build_try_statement PROTO ((int, tree, tree, tree));
static tree patch_try_statement PROTO ((tree));
void safe_layout_class PROTO ((tree));
void java_complete_class PROTO ((void));
......@@ -586,6 +622,8 @@ void java_check_methods PROTO ((void));
void java_layout_classes PROTO ((void));
tree java_method_add_stmt PROTO ((tree, tree));
char *java_get_line_col PROTO ((char *, int, int));
void java_expand_switch PROTO ((tree));
tree java_get_catch_block PROTO ((tree, int));
#endif /* JC1_LITE */
/* Always in use, no matter what you compile */
......
......@@ -930,14 +930,14 @@ verify_jvm_instructions (jcf, byte_ops, length)
case OPCODE_instanceof:
pop_type (ptr_type_node);
get_class_constant (current_jcf, IMMEDIATE_u2);
push_type (integer_type_node);
push_type (int_type_node);
break;
case OPCODE_tableswitch:
{
jint default_val, low, high;
pop_type (integer_type_node);
pop_type (int_type_node);
while (PC%4)
{
if (byte_ops[PC++])
......@@ -959,7 +959,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
{
jint npairs, last, not_registered = 1;
pop_type (integer_type_node);
pop_type (int_type_node);
while (PC%4)
{
if (byte_ops[PC++])
......
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