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