diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /gcc/java/expr.c | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'gcc/java/expr.c')
-rw-r--r-- | gcc/java/expr.c | 3820 |
1 files changed, 3820 insertions, 0 deletions
diff --git a/gcc/java/expr.c b/gcc/java/expr.c new file mode 100644 index 000000000..53feab5ce --- /dev/null +++ b/gcc/java/expr.c @@ -0,0 +1,3820 @@ +/* Process expressions for the GNU compiler for the Java(TM) language. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2010 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. + +Java and all Java-based marks are trademarks or registered trademarks +of Sun Microsystems, Inc. in the United States and other countries. +The Free Software Foundation is independent of Sun Microsystems, Inc. */ + +/* Hacked by Per Bothner <bothner@cygnus.com> February 1996. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "flags.h" +#include "java-tree.h" +#include "javaop.h" +#include "java-opcodes.h" +#include "jcf.h" +#include "java-except.h" +#include "parse.h" +#include "diagnostic-core.h" +#include "ggc.h" +#include "tree-iterator.h" +#include "target.h" + +static void flush_quick_stack (void); +static void push_value (tree); +static tree pop_value (tree); +static void java_stack_swap (void); +static void java_stack_dup (int, int); +static void build_java_athrow (tree); +static void build_java_jsr (int, int); +static void build_java_ret (tree); +static void expand_java_multianewarray (tree, int); +static void expand_java_arraystore (tree); +static void expand_java_arrayload (tree); +static void expand_java_array_length (void); +static tree build_java_monitor (tree, tree); +static void expand_java_pushc (int, tree); +static void expand_java_return (tree); +static void expand_load_internal (int, tree, int); +static void expand_java_NEW (tree); +static void expand_java_INSTANCEOF (tree); +static void expand_java_CHECKCAST (tree); +static void expand_iinc (unsigned int, int, int); +static void expand_java_binop (tree, enum tree_code); +static void note_label (int, int); +static void expand_compare (enum tree_code, tree, tree, int); +static void expand_test (enum tree_code, tree, int); +static void expand_cond (enum tree_code, tree, int); +static void expand_java_goto (int); +static tree expand_java_switch (tree, int); +static void expand_java_add_case (tree, int, int); +static VEC(tree,gc) *pop_arguments (tree); +static void expand_invoke (int, int, int); +static void expand_java_field_op (int, int, int); +static void java_push_constant_from_pool (struct JCF *, int); +static void java_stack_pop (int); +static tree build_java_throw_out_of_bounds_exception (tree); +static tree build_java_check_indexed_type (tree, tree); +static unsigned char peek_opcode_at_pc (struct JCF *, int, int); +static void promote_arguments (void); +static void cache_cpool_data_ref (void); + +static GTY(()) tree operand_type[59]; + +static GTY(()) tree methods_ident; +static GTY(()) tree ncode_ident; +tree dtable_ident = NULL_TREE; + +/* Set to nonzero value in order to emit class initialization code + before static field references. */ +int always_initialize_class_p = 0; + +/* We store the stack state in two places: + Within a basic block, we use the quick_stack, which is a VEC of expression + nodes. + This is the top part of the stack; below that we use find_stack_slot. + At the end of a basic block, the quick_stack must be flushed + to the stack slot array (as handled by find_stack_slot). + Using quick_stack generates better code (especially when + compiled without optimization), because we do not have to + explicitly store and load trees to temporary variables. + + If a variable is on the quick stack, it means the value of variable + when the quick stack was last flushed. Conceptually, flush_quick_stack + saves all the quick_stack elements in parallel. However, that is + complicated, so it actually saves them (i.e. copies each stack value + to is home virtual register) from low indexes. This allows a quick_stack + element at index i (counting from the bottom of stack the) to references + slot virtuals for register that are >= i, but not those that are deeper. + This convention makes most operations easier. For example iadd works + even when the stack contains (reg[0], reg[1]): It results in the + stack containing (reg[0]+reg[1]), which is OK. However, some stack + operations are more complicated. For example dup given a stack + containing (reg[0]) would yield (reg[0], reg[0]), which would violate + the convention, since stack value 1 would refer to a register with + lower index (reg[0]), which flush_quick_stack does not safely handle. + So dup cannot just add an extra element to the quick_stack, but iadd can. +*/ + +static GTY(()) VEC(tree,gc) *quick_stack; + +/* The physical memory page size used in this computer. See + build_field_ref(). */ +static GTY(()) tree page_size; + +/* The stack pointer of the Java virtual machine. + This does include the size of the quick_stack. */ + +int stack_pointer; + +const unsigned char *linenumber_table; +int linenumber_count; + +/* Largest pc so far in this method that has been passed to lookup_label. */ +int highest_label_pc_this_method = -1; + +/* Base value for this method to add to pc to get generated label. */ +int start_label_pc_this_method = 0; + +void +init_expr_processing (void) +{ + operand_type[21] = operand_type[54] = int_type_node; + operand_type[22] = operand_type[55] = long_type_node; + operand_type[23] = operand_type[56] = float_type_node; + operand_type[24] = operand_type[57] = double_type_node; + operand_type[25] = operand_type[58] = ptr_type_node; +} + +tree +java_truthvalue_conversion (tree expr) +{ + /* It is simpler and generates better code to have only TRUTH_*_EXPR + or comparison expressions as truth values at this level. + + This function should normally be identity for Java. */ + + switch (TREE_CODE (expr)) + { + case EQ_EXPR: case NE_EXPR: case UNEQ_EXPR: case LTGT_EXPR: + case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: + case UNLE_EXPR: case UNGE_EXPR: case UNLT_EXPR: case UNGT_EXPR: + case ORDERED_EXPR: case UNORDERED_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case TRUTH_NOT_EXPR: + case ERROR_MARK: + return expr; + + case INTEGER_CST: + return integer_zerop (expr) ? boolean_false_node : boolean_true_node; + + case REAL_CST: + return real_zerop (expr) ? boolean_false_node : boolean_true_node; + + /* are these legal? XXX JH */ + case NEGATE_EXPR: + case ABS_EXPR: + case FLOAT_EXPR: + /* These don't change whether an object is nonzero or zero. */ + return java_truthvalue_conversion (TREE_OPERAND (expr, 0)); + + case COND_EXPR: + /* Distribute the conversion into the arms of a COND_EXPR. */ + return fold_build3 (COND_EXPR, boolean_type_node, TREE_OPERAND (expr, 0), + java_truthvalue_conversion (TREE_OPERAND (expr, 1)), + java_truthvalue_conversion (TREE_OPERAND (expr, 2))); + + case NOP_EXPR: + /* If this is widening the argument, we can ignore it. */ + if (TYPE_PRECISION (TREE_TYPE (expr)) + >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0)))) + return java_truthvalue_conversion (TREE_OPERAND (expr, 0)); + /* fall through to default */ + + default: + return fold_build2 (NE_EXPR, boolean_type_node, + expr, boolean_false_node); + } +} + +/* Save any stack slots that happen to be in the quick_stack into their + home virtual register slots. + + The copy order is from low stack index to high, to support the invariant + that the expression for a slot may contain decls for stack slots with + higher (or the same) index, but not lower. */ + +static void +flush_quick_stack (void) +{ + int stack_index = stack_pointer; + unsigned ix; + tree t; + + /* Count the number of slots the quick stack is holding. */ + for (ix = 0; VEC_iterate(tree, quick_stack, ix, t); ix++) + stack_index -= 1 + TYPE_IS_WIDE (TREE_TYPE (t)); + + for (ix = 0; VEC_iterate(tree, quick_stack, ix, t); ix++) + { + tree decl, type = TREE_TYPE (t); + + decl = find_stack_slot (stack_index, type); + if (decl != t) + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (t), decl, t)); + stack_index += 1 + TYPE_IS_WIDE (type); + } + + VEC_truncate (tree, quick_stack, 0); +} + +/* Push TYPE on the type stack. + Return true on success, 0 on overflow. */ + +int +push_type_0 (tree type) +{ + int n_words; + type = promote_type (type); + n_words = 1 + TYPE_IS_WIDE (type); + if (stack_pointer + n_words > DECL_MAX_STACK (current_function_decl)) + return 0; + /* Allocate decl for this variable now, so we get a temporary that + survives the whole method. */ + find_stack_slot (stack_pointer, type); + stack_type_map[stack_pointer++] = type; + n_words--; + while (--n_words >= 0) + stack_type_map[stack_pointer++] = TYPE_SECOND; + return 1; +} + +void +push_type (tree type) +{ + int r = push_type_0 (type); + gcc_assert (r); +} + +static void +push_value (tree value) +{ + tree type = TREE_TYPE (value); + if (TYPE_PRECISION (type) < 32 && INTEGRAL_TYPE_P (type)) + { + type = promote_type (type); + value = convert (type, value); + } + push_type (type); + VEC_safe_push (tree, gc, quick_stack, value); + + /* If the value has a side effect, then we need to evaluate it + whether or not the result is used. If the value ends up on the + quick stack and is then popped, this won't happen -- so we flush + the quick stack. It is safest to simply always flush, though, + since TREE_SIDE_EFFECTS doesn't capture COMPONENT_REF, and for + the latter we may need to strip conversions. */ + flush_quick_stack (); +} + +/* Pop a type from the type stack. + TYPE is the expected type. Return the actual type, which must be + convertible to TYPE. + On an error, *MESSAGEP is set to a freshly malloc'd error message. */ + +tree +pop_type_0 (tree type, char **messagep) +{ + int n_words; + tree t; + *messagep = NULL; + if (TREE_CODE (type) == RECORD_TYPE) + type = promote_type (type); + n_words = 1 + TYPE_IS_WIDE (type); + if (stack_pointer < n_words) + { + *messagep = xstrdup ("stack underflow"); + return type; + } + while (--n_words > 0) + { + if (stack_type_map[--stack_pointer] != void_type_node) + { + *messagep = xstrdup ("Invalid multi-word value on type stack"); + return type; + } + } + t = stack_type_map[--stack_pointer]; + if (type == NULL_TREE || t == type) + return t; + if (TREE_CODE (t) == TREE_LIST) + { + do + { + tree tt = TREE_PURPOSE (t); + if (! can_widen_reference_to (tt, type)) + { + t = tt; + goto fail; + } + t = TREE_CHAIN (t); + } + while (t); + return t; + } + if (INTEGRAL_TYPE_P (type) && INTEGRAL_TYPE_P (t) + && TYPE_PRECISION (type) <= 32 && TYPE_PRECISION (t) <= 32) + return t; + if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (t) == POINTER_TYPE) + { + /* If the expected type we've been passed is object or ptr + (i.e. void*), the caller needs to know the real type. */ + if (type == ptr_type_node || type == object_ptr_type_node) + return t; + + /* Since the verifier has already run, we know that any + types we see will be compatible. In BC mode, this fact + may be checked at runtime, but if that is so then we can + assume its truth here as well. So, we always succeed + here, with the expected type. */ + return type; + } + + if (! flag_verify_invocations && flag_indirect_dispatch + && t == object_ptr_type_node) + { + if (type != ptr_type_node) + warning (0, "need to insert runtime check for %s", + xstrdup (lang_printable_name (type, 0))); + return type; + } + + /* lang_printable_name uses a static buffer, so we must save the result + from calling it the first time. */ + fail: + { + char *temp = xstrdup (lang_printable_name (type, 0)); + /* If the stack contains a multi-word type, keep popping the stack until + the real type is found. */ + while (t == void_type_node) + t = stack_type_map[--stack_pointer]; + *messagep = concat ("expected type '", temp, + "' but stack contains '", lang_printable_name (t, 0), + "'", NULL); + free (temp); + } + return type; +} + +/* Pop a type from the type stack. + TYPE is the expected type. Return the actual type, which must be + convertible to TYPE, otherwise call error. */ + +tree +pop_type (tree type) +{ + char *message = NULL; + type = pop_type_0 (type, &message); + if (message != NULL) + { + error ("%s", message); + free (message); + } + return type; +} + + +/* Return true if two type assertions are equal. */ + +static int +type_assertion_eq (const void * k1_p, const void * k2_p) +{ + const type_assertion k1 = *(const type_assertion *)k1_p; + const type_assertion k2 = *(const type_assertion *)k2_p; + return (k1.assertion_code == k2.assertion_code + && k1.op1 == k2.op1 + && k1.op2 == k2.op2); +} + +/* Hash a type assertion. */ + +static hashval_t +type_assertion_hash (const void *p) +{ + const type_assertion *k_p = (const type_assertion *) p; + hashval_t hash = iterative_hash (&k_p->assertion_code, sizeof + k_p->assertion_code, 0); + + switch (k_p->assertion_code) + { + case JV_ASSERT_TYPES_COMPATIBLE: + hash = iterative_hash (&TYPE_UID (k_p->op2), sizeof TYPE_UID (k_p->op2), + hash); + /* Fall through. */ + + case JV_ASSERT_IS_INSTANTIABLE: + hash = iterative_hash (&TYPE_UID (k_p->op1), sizeof TYPE_UID (k_p->op1), + hash); + /* Fall through. */ + + case JV_ASSERT_END_OF_TABLE: + break; + + default: + gcc_unreachable (); + } + + return hash; +} + +/* Add an entry to the type assertion table for the given class. + KLASS is the class for which this assertion will be evaluated by the + runtime during loading/initialization. + ASSERTION_CODE is the 'opcode' or type of this assertion: see java-tree.h. + OP1 and OP2 are the operands. The tree type of these arguments may be + specific to each assertion_code. */ + +void +add_type_assertion (tree klass, int assertion_code, tree op1, tree op2) +{ + htab_t assertions_htab; + type_assertion as; + void **as_pp; + + assertions_htab = TYPE_ASSERTIONS (klass); + if (assertions_htab == NULL) + { + assertions_htab = htab_create_ggc (7, type_assertion_hash, + type_assertion_eq, NULL); + TYPE_ASSERTIONS (current_class) = assertions_htab; + } + + as.assertion_code = assertion_code; + as.op1 = op1; + as.op2 = op2; + + as_pp = htab_find_slot (assertions_htab, &as, INSERT); + + /* Don't add the same assertion twice. */ + if (*as_pp) + return; + + *as_pp = ggc_alloc_type_assertion (); + **(type_assertion **)as_pp = as; +} + + +/* Return 1 if SOURCE_TYPE can be safely widened to TARGET_TYPE. + Handles array types and interfaces. */ + +int +can_widen_reference_to (tree source_type, tree target_type) +{ + if (source_type == ptr_type_node || target_type == object_ptr_type_node) + return 1; + + /* Get rid of pointers */ + if (TREE_CODE (source_type) == POINTER_TYPE) + source_type = TREE_TYPE (source_type); + if (TREE_CODE (target_type) == POINTER_TYPE) + target_type = TREE_TYPE (target_type); + + if (source_type == target_type) + return 1; + + /* FIXME: This is very pessimistic, in that it checks everything, + even if we already know that the types are compatible. If we're + to support full Java class loader semantics, we need this. + However, we could do something more optimal. */ + if (! flag_verify_invocations) + { + add_type_assertion (current_class, JV_ASSERT_TYPES_COMPATIBLE, + source_type, target_type); + + if (!quiet_flag) + warning (0, "assert: %s is assign compatible with %s", + xstrdup (lang_printable_name (target_type, 0)), + xstrdup (lang_printable_name (source_type, 0))); + /* Punt everything to runtime. */ + return 1; + } + + if (TYPE_DUMMY (source_type) || TYPE_DUMMY (target_type)) + { + return 1; + } + else + { + if (TYPE_ARRAY_P (source_type) || TYPE_ARRAY_P (target_type)) + { + HOST_WIDE_INT source_length, target_length; + if (TYPE_ARRAY_P (source_type) != TYPE_ARRAY_P (target_type)) + { + /* An array implements Cloneable and Serializable. */ + tree name = DECL_NAME (TYPE_NAME (target_type)); + return (name == java_lang_cloneable_identifier_node + || name == java_io_serializable_identifier_node); + } + target_length = java_array_type_length (target_type); + if (target_length >= 0) + { + source_length = java_array_type_length (source_type); + if (source_length != target_length) + return 0; + } + source_type = TYPE_ARRAY_ELEMENT (source_type); + target_type = TYPE_ARRAY_ELEMENT (target_type); + if (source_type == target_type) + return 1; + if (TREE_CODE (source_type) != POINTER_TYPE + || TREE_CODE (target_type) != POINTER_TYPE) + return 0; + return can_widen_reference_to (source_type, target_type); + } + else + { + int source_depth = class_depth (source_type); + int target_depth = class_depth (target_type); + + if (TYPE_DUMMY (source_type) || TYPE_DUMMY (target_type)) + { + if (! quiet_flag) + warning (0, "assert: %s is assign compatible with %s", + xstrdup (lang_printable_name (target_type, 0)), + xstrdup (lang_printable_name (source_type, 0))); + return 1; + } + + /* class_depth can return a negative depth if an error occurred */ + if (source_depth < 0 || target_depth < 0) + return 0; + + if (CLASS_INTERFACE (TYPE_NAME (target_type))) + { + /* target_type is OK if source_type or source_type ancestors + implement target_type. We handle multiple sub-interfaces */ + tree binfo, base_binfo; + int i; + + for (binfo = TYPE_BINFO (source_type), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + if (can_widen_reference_to + (BINFO_TYPE (base_binfo), target_type)) + return 1; + + if (!i) + return 0; + } + + for ( ; source_depth > target_depth; source_depth--) + { + source_type + = BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (source_type), 0)); + } + return source_type == target_type; + } + } +} + +static tree +pop_value (tree type) +{ + type = pop_type (type); + if (VEC_length (tree, quick_stack) != 0) + return VEC_pop (tree, quick_stack); + else + return find_stack_slot (stack_pointer, promote_type (type)); +} + + +/* Pop and discard the top COUNT stack slots. */ + +static void +java_stack_pop (int count) +{ + while (count > 0) + { + tree type; + + gcc_assert (stack_pointer != 0); + + type = stack_type_map[stack_pointer - 1]; + if (type == TYPE_SECOND) + { + count--; + gcc_assert (stack_pointer != 1 && count > 0); + + type = stack_type_map[stack_pointer - 2]; + } + pop_value (type); + count--; + } +} + +/* Implement the 'swap' operator (to swap two top stack slots). */ + +static void +java_stack_swap (void) +{ + tree type1, type2; + tree temp; + tree decl1, decl2; + + if (stack_pointer < 2 + || (type1 = stack_type_map[stack_pointer - 1]) == TYPE_SECOND + || (type2 = stack_type_map[stack_pointer - 2]) == TYPE_SECOND + || TYPE_IS_WIDE (type1) || TYPE_IS_WIDE (type2)) + /* Bad stack swap. */ + abort (); + /* Bad stack swap. */ + + flush_quick_stack (); + decl1 = find_stack_slot (stack_pointer - 1, type1); + decl2 = find_stack_slot (stack_pointer - 2, type2); + temp = build_decl (input_location, VAR_DECL, NULL_TREE, type1); + java_add_local_var (temp); + java_add_stmt (build2 (MODIFY_EXPR, type1, temp, decl1)); + java_add_stmt (build2 (MODIFY_EXPR, type2, + find_stack_slot (stack_pointer - 1, type2), + decl2)); + java_add_stmt (build2 (MODIFY_EXPR, type1, + find_stack_slot (stack_pointer - 2, type1), + temp)); + stack_type_map[stack_pointer - 1] = type2; + stack_type_map[stack_pointer - 2] = type1; +} + +static void +java_stack_dup (int size, int offset) +{ + int low_index = stack_pointer - size - offset; + int dst_index; + if (low_index < 0) + error ("stack underflow - dup* operation"); + + flush_quick_stack (); + + stack_pointer += size; + dst_index = stack_pointer; + + for (dst_index = stack_pointer; --dst_index >= low_index; ) + { + tree type; + int src_index = dst_index - size; + if (src_index < low_index) + src_index = dst_index + size + offset; + type = stack_type_map [src_index]; + if (type == TYPE_SECOND) + { + /* Dup operation splits 64-bit number. */ + gcc_assert (src_index > low_index); + + stack_type_map[dst_index] = type; + src_index--; dst_index--; + type = stack_type_map[src_index]; + gcc_assert (TYPE_IS_WIDE (type)); + } + else + gcc_assert (! TYPE_IS_WIDE (type)); + + if (src_index != dst_index) + { + tree src_decl = find_stack_slot (src_index, type); + tree dst_decl = find_stack_slot (dst_index, type); + + java_add_stmt + (build2 (MODIFY_EXPR, TREE_TYPE (dst_decl), dst_decl, src_decl)); + stack_type_map[dst_index] = type; + } + } +} + +/* Calls _Jv_Throw or _Jv_Sjlj_Throw. Discard the contents of the + value stack. */ + +static void +build_java_athrow (tree node) +{ + tree call; + + call = build_call_nary (void_type_node, + build_address_of (throw_node), + 1, node); + TREE_SIDE_EFFECTS (call) = 1; + java_add_stmt (call); + java_stack_pop (stack_pointer); +} + +/* Implementation for jsr/ret */ + +static void +build_java_jsr (int target_pc, int return_pc) +{ + tree where = lookup_label (target_pc); + tree ret = lookup_label (return_pc); + tree ret_label = fold_build1 (ADDR_EXPR, return_address_type_node, ret); + push_value (ret_label); + flush_quick_stack (); + java_add_stmt (build1 (GOTO_EXPR, void_type_node, where)); + + /* Do not need to emit the label here. We noted the existence of the + label as a jump target in note_instructions; we'll emit the label + for real at the beginning of the expand_byte_code loop. */ +} + +static void +build_java_ret (tree location) +{ + java_add_stmt (build1 (GOTO_EXPR, void_type_node, location)); +} + +/* Implementation of operations on array: new, load, store, length */ + +tree +decode_newarray_type (int atype) +{ + switch (atype) + { + case 4: return boolean_type_node; + case 5: return char_type_node; + case 6: return float_type_node; + case 7: return double_type_node; + case 8: return byte_type_node; + case 9: return short_type_node; + case 10: return int_type_node; + case 11: return long_type_node; + default: return NULL_TREE; + } +} + +/* Map primitive type to the code used by OPCODE_newarray. */ + +int +encode_newarray_type (tree type) +{ + if (type == boolean_type_node) + return 4; + else if (type == char_type_node) + return 5; + else if (type == float_type_node) + return 6; + else if (type == double_type_node) + return 7; + else if (type == byte_type_node) + return 8; + else if (type == short_type_node) + return 9; + else if (type == int_type_node) + return 10; + else if (type == long_type_node) + return 11; + else + gcc_unreachable (); +} + +/* Build a call to _Jv_ThrowBadArrayIndex(), the + ArrayIndexOfBoundsException exception handler. */ + +static tree +build_java_throw_out_of_bounds_exception (tree index) +{ + tree node; + + /* We need to build a COMPOUND_EXPR because _Jv_ThrowBadArrayIndex() + has void return type. We cannot just set the type of the CALL_EXPR below + to int_type_node because we would lose it during gimplification. */ + gcc_assert (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (soft_badarrayindex_node)))); + node = build_call_nary (void_type_node, + build_address_of (soft_badarrayindex_node), + 1, index); + TREE_SIDE_EFFECTS (node) = 1; + + node = build2 (COMPOUND_EXPR, int_type_node, node, integer_zero_node); + TREE_SIDE_EFFECTS (node) = 1; /* Allows expansion within ANDIF */ + + return (node); +} + +/* Return the length of an array. Doesn't perform any checking on the nature + or value of the array NODE. May be used to implement some bytecodes. */ + +tree +build_java_array_length_access (tree node) +{ + tree type = TREE_TYPE (node); + tree array_type = TREE_TYPE (type); + HOST_WIDE_INT length; + + if (!is_array_type_p (type)) + { + /* With the new verifier, we will see an ordinary pointer type + here. In this case, we just use an arbitrary array type. */ + array_type = build_java_array_type (object_ptr_type_node, -1); + type = promote_type (array_type); + } + + length = java_array_type_length (type); + if (length >= 0) + return build_int_cst (NULL_TREE, length); + + node = build3 (COMPONENT_REF, int_type_node, + build_java_indirect_ref (array_type, node, + flag_check_references), + lookup_field (&array_type, get_identifier ("length")), + NULL_TREE); + IS_ARRAY_LENGTH_ACCESS (node) = 1; + return node; +} + +/* Optionally checks a reference against the NULL pointer. ARG1: the + expr, ARG2: we should check the reference. Don't generate extra + checks if we're not generating code. */ + +tree +java_check_reference (tree expr, int check) +{ + if (!flag_syntax_only && check) + { + expr = save_expr (expr); + expr = build3 (COND_EXPR, TREE_TYPE (expr), + build2 (EQ_EXPR, boolean_type_node, + expr, null_pointer_node), + build_call_nary (void_type_node, + build_address_of (soft_nullpointer_node), + 0), + expr); + } + + return expr; +} + +/* Reference an object: just like an INDIRECT_REF, but with checking. */ + +tree +build_java_indirect_ref (tree type, tree expr, int check) +{ + tree t; + t = java_check_reference (expr, check); + t = convert (build_pointer_type (type), t); + return build1 (INDIRECT_REF, type, t); +} + +/* Implement array indexing (either as l-value or r-value). + Returns a tree for ARRAY[INDEX], assume TYPE is the element type. + Optionally performs bounds checking and/or test to NULL. + At this point, ARRAY should have been verified as an array. */ + +tree +build_java_arrayaccess (tree array, tree type, tree index) +{ + tree node, throw_expr = NULL_TREE; + tree data_field; + tree ref; + tree array_type = TREE_TYPE (TREE_TYPE (array)); + tree size_exp = fold_convert (sizetype, size_in_bytes (type)); + + if (!is_array_type_p (TREE_TYPE (array))) + { + /* With the new verifier, we will see an ordinary pointer type + here. In this case, we just use the correct array type. */ + array_type = build_java_array_type (type, -1); + } + + if (flag_bounds_check) + { + /* Generate: + * (unsigned jint) INDEX >= (unsigned jint) LEN + * && throw ArrayIndexOutOfBoundsException. + * Note this is equivalent to and more efficient than: + * INDEX < 0 || INDEX >= LEN && throw ... */ + tree test; + tree len = convert (unsigned_int_type_node, + build_java_array_length_access (array)); + test = fold_build2 (GE_EXPR, boolean_type_node, + convert (unsigned_int_type_node, index), + len); + if (! integer_zerop (test)) + { + throw_expr + = build2 (TRUTH_ANDIF_EXPR, int_type_node, test, + build_java_throw_out_of_bounds_exception (index)); + /* allows expansion within COMPOUND */ + TREE_SIDE_EFFECTS( throw_expr ) = 1; + } + } + + /* If checking bounds, wrap the index expr with a COMPOUND_EXPR in order + to have the bounds check evaluated first. */ + if (throw_expr != NULL_TREE) + index = build2 (COMPOUND_EXPR, int_type_node, throw_expr, index); + + data_field = lookup_field (&array_type, get_identifier ("data")); + + ref = build3 (COMPONENT_REF, TREE_TYPE (data_field), + build_java_indirect_ref (array_type, array, + flag_check_references), + data_field, NULL_TREE); + + /* Take the address of the data field and convert it to a pointer to + the element type. */ + node = build1 (NOP_EXPR, build_pointer_type (type), build_address_of (ref)); + + /* Multiply the index by the size of an element to obtain a byte + offset. Convert the result to a pointer to the element type. */ + index = build2 (MULT_EXPR, sizetype, + fold_convert (sizetype, index), + size_exp); + + /* Sum the byte offset and the address of the data field. */ + node = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (node), node, index); + + /* Finally, return + + *((&array->data) + index*size_exp) + + */ + return build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (node)), node); +} + +/* Generate code to throw an ArrayStoreException if OBJECT is not assignable + (at runtime) to an element of ARRAY. A NOP_EXPR is returned if it can + determine that no check is required. */ + +tree +build_java_arraystore_check (tree array, tree object) +{ + tree check, element_type, source; + tree array_type_p = TREE_TYPE (array); + tree object_type = TYPE_NAME (TREE_TYPE (TREE_TYPE (object))); + + if (! flag_verify_invocations) + { + /* With the new verifier, we don't track precise types. FIXME: + performance regression here. */ + element_type = TYPE_NAME (object_type_node); + } + else + { + gcc_assert (is_array_type_p (array_type_p)); + + /* Get the TYPE_DECL for ARRAY's element type. */ + element_type + = TYPE_NAME (TREE_TYPE (TREE_TYPE (TREE_TYPE (array_type_p)))); + } + + gcc_assert (TREE_CODE (element_type) == TYPE_DECL + && TREE_CODE (object_type) == TYPE_DECL); + + if (!flag_store_check) + return build1 (NOP_EXPR, array_type_p, array); + + /* No check is needed if the element type is final. Also check that + element_type matches object_type, since in the bytecode + compilation case element_type may be the actual element type of + the array rather than its declared type. However, if we're doing + indirect dispatch, we can't do the `final' optimization. */ + if (element_type == object_type + && ! flag_indirect_dispatch + && CLASS_FINAL (element_type)) + return build1 (NOP_EXPR, array_type_p, array); + + /* OBJECT might be wrapped by a SAVE_EXPR. */ + if (TREE_CODE (object) == SAVE_EXPR) + source = TREE_OPERAND (object, 0); + else + source = object; + + /* Avoid the check if OBJECT was just loaded from the same array. */ + if (TREE_CODE (source) == ARRAY_REF) + { + tree target; + source = TREE_OPERAND (source, 0); /* COMPONENT_REF. */ + source = TREE_OPERAND (source, 0); /* INDIRECT_REF. */ + source = TREE_OPERAND (source, 0); /* Source array's DECL or SAVE_EXPR. */ + if (TREE_CODE (source) == SAVE_EXPR) + source = TREE_OPERAND (source, 0); + + target = array; + if (TREE_CODE (target) == SAVE_EXPR) + target = TREE_OPERAND (target, 0); + + if (source == target) + return build1 (NOP_EXPR, array_type_p, array); + } + + /* Build an invocation of _Jv_CheckArrayStore */ + check = build_call_nary (void_type_node, + build_address_of (soft_checkarraystore_node), + 2, array, object); + TREE_SIDE_EFFECTS (check) = 1; + + return check; +} + +/* Makes sure that INDEXED_TYPE is appropriate. If not, make it from + ARRAY_NODE. This function is used to retrieve something less vague than + a pointer type when indexing the first dimension of something like [[<t>. + May return a corrected type, if necessary, otherwise INDEXED_TYPE is + return unchanged. */ + +static tree +build_java_check_indexed_type (tree array_node ATTRIBUTE_UNUSED, + tree indexed_type) +{ + /* We used to check to see if ARRAY_NODE really had array type. + However, with the new verifier, this is not necessary, as we know + that the object will be an array of the appropriate type. */ + + return indexed_type; +} + +/* newarray triggers a call to _Jv_NewPrimArray. This function should be + called with an integer code (the type of array to create), and the length + of the array to create. */ + +tree +build_newarray (int atype_value, tree length) +{ + tree type_arg; + + tree prim_type = decode_newarray_type (atype_value); + tree type + = build_java_array_type (prim_type, + host_integerp (length, 0) == INTEGER_CST + ? tree_low_cst (length, 0) : -1); + + /* Pass a reference to the primitive type class and save the runtime + some work. */ + type_arg = build_class_ref (prim_type); + + return build_call_nary (promote_type (type), + build_address_of (soft_newarray_node), + 2, type_arg, length); +} + +/* Generates anewarray from a given CLASS_TYPE. Gets from the stack the size + of the dimension. */ + +tree +build_anewarray (tree class_type, tree length) +{ + tree type + = build_java_array_type (class_type, + host_integerp (length, 0) + ? tree_low_cst (length, 0) : -1); + + return build_call_nary (promote_type (type), + build_address_of (soft_anewarray_node), + 3, + length, + build_class_ref (class_type), + null_pointer_node); +} + +/* Return a node the evaluates 'new TYPE[LENGTH]'. */ + +tree +build_new_array (tree type, tree length) +{ + if (JPRIMITIVE_TYPE_P (type)) + return build_newarray (encode_newarray_type (type), length); + else + return build_anewarray (TREE_TYPE (type), length); +} + +/* 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. */ + +static void +expand_java_multianewarray (tree class_type, int ndim) +{ + int i; + VEC(tree,gc) *args = NULL; + + VEC_safe_grow (tree, gc, args, 3 + ndim); + + VEC_replace (tree, args, 0, build_class_ref (class_type)); + VEC_replace (tree, args, 1, build_int_cst (NULL_TREE, ndim)); + + for(i = ndim - 1; i >= 0; i-- ) + VEC_replace (tree, args, (unsigned)(2 + i), pop_value (int_type_node)); + + VEC_replace (tree, args, 2 + ndim, null_pointer_node); + + push_value (build_call_vec (promote_type (class_type), + build_address_of (soft_multianewarray_node), + args)); +} + +/* ARRAY[INDEX] <- RHS. build_java_check_indexed_type makes sure that + ARRAY is an array type. May expand some bound checking and NULL + pointer checking. RHS_TYPE_NODE we are going to store. In the case + of the CHAR/BYTE/BOOLEAN SHORT, the type popped of the stack is an + INT. In those cases, we make the conversion. + + if ARRAy is a reference type, the assignment is checked at run-time + to make sure that the RHS can be assigned to the array element + type. It is not necessary to generate this code if ARRAY is final. */ + +static void +expand_java_arraystore (tree rhs_type_node) +{ + tree rhs_node = pop_value ((INTEGRAL_TYPE_P (rhs_type_node) + && TYPE_PRECISION (rhs_type_node) <= 32) ? + int_type_node : rhs_type_node); + tree index = pop_value (int_type_node); + tree array_type, array, temp, access; + + /* If we're processing an `aaload' we might as well just pick + `Object'. */ + if (TREE_CODE (rhs_type_node) == POINTER_TYPE) + { + array_type = build_java_array_type (object_ptr_type_node, -1); + rhs_type_node = object_ptr_type_node; + } + else + array_type = build_java_array_type (rhs_type_node, -1); + + array = pop_value (array_type); + array = build1 (NOP_EXPR, promote_type (array_type), array); + + rhs_type_node = build_java_check_indexed_type (array, rhs_type_node); + + flush_quick_stack (); + + index = save_expr (index); + array = save_expr (array); + + /* We want to perform the bounds check (done by + build_java_arrayaccess) before the type check (done by + build_java_arraystore_check). So, we call build_java_arrayaccess + -- which returns an ARRAY_REF lvalue -- and we then generate code + to stash the address of that lvalue in a temp. Then we call + build_java_arraystore_check, and finally we generate a + MODIFY_EXPR to set the array element. */ + + access = build_java_arrayaccess (array, rhs_type_node, index); + temp = build_decl (input_location, VAR_DECL, NULL_TREE, + build_pointer_type (TREE_TYPE (access))); + java_add_local_var (temp); + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (temp), + temp, + build_fold_addr_expr (access))); + + if (TREE_CODE (rhs_type_node) == POINTER_TYPE) + { + tree check = build_java_arraystore_check (array, rhs_node); + java_add_stmt (check); + } + + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (access), + build1 (INDIRECT_REF, TREE_TYPE (access), temp), + rhs_node)); +} + +/* Expand the evaluation of ARRAY[INDEX]. build_java_check_indexed_type makes + sure that LHS is an array type. May expand some bound checking and NULL + pointer checking. + LHS_TYPE_NODE is the type of ARRAY[INDEX]. But in the case of CHAR/BYTE/ + BOOLEAN/SHORT, we push a promoted type back to the stack. +*/ + +static void +expand_java_arrayload (tree lhs_type_node) +{ + tree load_node; + tree index_node = pop_value (int_type_node); + tree array_type; + tree array_node; + + /* If we're processing an `aaload' we might as well just pick + `Object'. */ + if (TREE_CODE (lhs_type_node) == POINTER_TYPE) + { + array_type = build_java_array_type (object_ptr_type_node, -1); + lhs_type_node = object_ptr_type_node; + } + else + array_type = build_java_array_type (lhs_type_node, -1); + array_node = pop_value (array_type); + array_node = build1 (NOP_EXPR, promote_type (array_type), array_node); + + index_node = save_expr (index_node); + array_node = save_expr (array_node); + + lhs_type_node = build_java_check_indexed_type (array_node, + lhs_type_node); + load_node = build_java_arrayaccess (array_node, + lhs_type_node, + index_node); + if (INTEGRAL_TYPE_P (lhs_type_node) && TYPE_PRECISION (lhs_type_node) <= 32) + load_node = fold_build1 (NOP_EXPR, int_type_node, load_node); + push_value (load_node); +} + +/* Expands .length. Makes sure that we deal with and array and may expand + a NULL check on the array object. */ + +static void +expand_java_array_length (void) +{ + tree array = pop_value (ptr_type_node); + tree length = build_java_array_length_access (array); + + push_value (length); +} + +/* Emit code for the call to _Jv_Monitor{Enter,Exit}. CALL can be + either soft_monitorenter_node or soft_monitorexit_node. */ + +static tree +build_java_monitor (tree call, tree object) +{ + return build_call_nary (void_type_node, + build_address_of (call), + 1, object); +} + +/* Emit code for one of the PUSHC instructions. */ + +static void +expand_java_pushc (int ival, tree type) +{ + tree value; + if (type == ptr_type_node && ival == 0) + value = null_pointer_node; + else if (type == int_type_node || type == long_type_node) + value = build_int_cst (type, ival); + else if (type == float_type_node || type == double_type_node) + { + REAL_VALUE_TYPE x; + REAL_VALUE_FROM_INT (x, ival, 0, TYPE_MODE (type)); + value = build_real (type, x); + } + else + gcc_unreachable (); + + push_value (value); +} + +static void +expand_java_return (tree type) +{ + if (type == void_type_node) + java_add_stmt (build1 (RETURN_EXPR, void_type_node, NULL)); + else + { + tree retval = pop_value (type); + tree res = DECL_RESULT (current_function_decl); + retval = build2 (MODIFY_EXPR, TREE_TYPE (res), res, retval); + + /* Handle the situation where the native integer type is smaller + than the JVM integer. It can happen for many cross compilers. + The whole if expression just goes away if INT_TYPE_SIZE < 32 + is false. */ + if (INT_TYPE_SIZE < 32 + && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (res))) + < GET_MODE_SIZE (TYPE_MODE (type)))) + retval = build1(NOP_EXPR, TREE_TYPE(res), retval); + + TREE_SIDE_EFFECTS (retval) = 1; + java_add_stmt (build1 (RETURN_EXPR, void_type_node, retval)); + } +} + +static void +expand_load_internal (int index, tree type, int pc) +{ + tree copy; + tree var = find_local_variable (index, type, pc); + + /* Now VAR is the VAR_DECL (or PARM_DECL) that we are going to push + on the stack. If there is an assignment to this VAR_DECL between + the stack push and the use, then the wrong code could be + generated. To avoid this we create a new local and copy our + value into it. Then we push this new local on the stack. + Hopefully this all gets optimized out. */ + copy = build_decl (input_location, VAR_DECL, NULL_TREE, type); + if ((INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)) + && TREE_TYPE (copy) != TREE_TYPE (var)) + var = convert (type, var); + java_add_local_var (copy); + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (var), copy, var)); + + push_value (copy); +} + +tree +build_address_of (tree value) +{ + return build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (value)), value); +} + +bool +class_has_finalize_method (tree type) +{ + tree super = CLASSTYPE_SUPER (type); + + if (super == NULL_TREE) + return false; /* Every class with a real finalizer inherits */ + /* from java.lang.Object. */ + else + return HAS_FINALIZER_P (type) || class_has_finalize_method (super); +} + +tree +java_create_object (tree type) +{ + tree alloc_node = (class_has_finalize_method (type) + ? alloc_object_node + : alloc_no_finalizer_node); + + return build_call_nary (promote_type (type), + build_address_of (alloc_node), + 1, build_class_ref (type)); +} + +static void +expand_java_NEW (tree type) +{ + tree alloc_node; + + alloc_node = (class_has_finalize_method (type) ? alloc_object_node + : alloc_no_finalizer_node); + if (! CLASS_LOADED_P (type)) + load_class (type, 1); + safe_layout_class (type); + push_value (build_call_nary (promote_type (type), + build_address_of (alloc_node), + 1, build_class_ref (type))); +} + +/* This returns an expression which will extract the class of an + object. */ + +tree +build_get_class (tree value) +{ + tree class_field = lookup_field (&dtable_type, get_identifier ("class")); + tree vtable_field = lookup_field (&object_type_node, + get_identifier ("vtable")); + tree tmp = build3 (COMPONENT_REF, dtable_ptr_type, + build_java_indirect_ref (object_type_node, value, + flag_check_references), + vtable_field, NULL_TREE); + return build3 (COMPONENT_REF, class_ptr_type, + build1 (INDIRECT_REF, dtable_type, tmp), + class_field, NULL_TREE); +} + +/* This builds the tree representation of the `instanceof' operator. + It tries various tricks to optimize this in cases where types are + known. */ + +tree +build_instanceof (tree value, tree type) +{ + tree expr; + tree itype = TREE_TYPE (TREE_TYPE (soft_instanceof_node)); + tree valtype = TREE_TYPE (TREE_TYPE (value)); + tree valclass = TYPE_NAME (valtype); + tree klass; + + /* When compiling from bytecode, we need to ensure that TYPE has + been loaded. */ + if (CLASS_P (type) && ! CLASS_LOADED_P (type)) + { + load_class (type, 1); + safe_layout_class (type); + if (! TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) == ERROR_MARK) + return error_mark_node; + } + klass = TYPE_NAME (type); + + if (type == object_type_node || inherits_from_p (valtype, type)) + { + /* Anything except `null' is an instance of Object. Likewise, + if the object is known to be an instance of the class, then + we only need to check for `null'. */ + expr = build2 (NE_EXPR, itype, value, null_pointer_node); + } + else if (flag_verify_invocations + && ! TYPE_ARRAY_P (type) + && ! TYPE_ARRAY_P (valtype) + && DECL_P (klass) && DECL_P (valclass) + && ! CLASS_INTERFACE (valclass) + && ! CLASS_INTERFACE (klass) + && ! inherits_from_p (type, valtype) + && (CLASS_FINAL (klass) + || ! inherits_from_p (valtype, type))) + { + /* The classes are from different branches of the derivation + tree, so we immediately know the answer. */ + expr = boolean_false_node; + } + else if (DECL_P (klass) && CLASS_FINAL (klass)) + { + tree save = save_expr (value); + expr = build3 (COND_EXPR, itype, + build2 (NE_EXPR, boolean_type_node, + save, null_pointer_node), + build2 (EQ_EXPR, itype, + build_get_class (save), + build_class_ref (type)), + boolean_false_node); + } + else + { + expr = build_call_nary (itype, + build_address_of (soft_instanceof_node), + 2, value, build_class_ref (type)); + } + TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (value); + return expr; +} + +static void +expand_java_INSTANCEOF (tree type) +{ + tree value = pop_value (object_ptr_type_node); + value = build_instanceof (value, type); + push_value (value); +} + +static void +expand_java_CHECKCAST (tree type) +{ + tree value = pop_value (ptr_type_node); + value = build_call_nary (promote_type (type), + build_address_of (soft_checkcast_node), + 2, build_class_ref (type), value); + push_value (value); +} + +static void +expand_iinc (unsigned int local_var_index, int ival, int pc) +{ + tree local_var, res; + tree constant_value; + + flush_quick_stack (); + local_var = find_local_variable (local_var_index, int_type_node, pc); + constant_value = build_int_cst (NULL_TREE, ival); + res = fold_build2 (PLUS_EXPR, int_type_node, local_var, constant_value); + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (local_var), local_var, res)); +} + + +tree +build_java_soft_divmod (enum tree_code op, tree type, tree op1, tree op2) +{ + tree call = NULL; + tree arg1 = convert (type, op1); + tree arg2 = convert (type, op2); + + if (type == int_type_node) + { + switch (op) + { + case TRUNC_DIV_EXPR: + call = soft_idiv_node; + break; + case TRUNC_MOD_EXPR: + call = soft_irem_node; + break; + default: + break; + } + } + else if (type == long_type_node) + { + switch (op) + { + case TRUNC_DIV_EXPR: + call = soft_ldiv_node; + break; + case TRUNC_MOD_EXPR: + call = soft_lrem_node; + break; + default: + break; + } + } + + gcc_assert (call); + call = build_call_nary (type, build_address_of (call), 2, arg1, arg2); + return call; +} + +tree +build_java_binop (enum tree_code op, tree type, tree arg1, tree arg2) +{ + tree mask; + switch (op) + { + case URSHIFT_EXPR: + { + tree u_type = unsigned_type_for (type); + arg1 = convert (u_type, arg1); + arg1 = build_java_binop (RSHIFT_EXPR, u_type, arg1, arg2); + return convert (type, arg1); + } + case LSHIFT_EXPR: + case RSHIFT_EXPR: + mask = build_int_cst (NULL_TREE, + TYPE_PRECISION (TREE_TYPE (arg1)) - 1); + arg2 = fold_build2 (BIT_AND_EXPR, int_type_node, arg2, mask); + break; + + case COMPARE_L_EXPR: /* arg1 > arg2 ? 1 : arg1 == arg2 ? 0 : -1 */ + case COMPARE_G_EXPR: /* arg1 < arg2 ? -1 : arg1 == arg2 ? 0 : 1 */ + arg1 = save_expr (arg1); arg2 = save_expr (arg2); + { + tree ifexp1 = fold_build2 (op == COMPARE_L_EXPR ? GT_EXPR : LT_EXPR, + boolean_type_node, arg1, arg2); + tree ifexp2 = fold_build2 (EQ_EXPR, boolean_type_node, arg1, arg2); + tree second_compare = fold_build3 (COND_EXPR, int_type_node, + ifexp2, integer_zero_node, + op == COMPARE_L_EXPR + ? integer_minus_one_node + : integer_one_node); + return fold_build3 (COND_EXPR, int_type_node, ifexp1, + op == COMPARE_L_EXPR ? integer_one_node + : integer_minus_one_node, + second_compare); + } + case COMPARE_EXPR: + arg1 = save_expr (arg1); arg2 = save_expr (arg2); + { + tree ifexp1 = fold_build2 (LT_EXPR, boolean_type_node, arg1, arg2); + tree ifexp2 = fold_build2 (GT_EXPR, boolean_type_node, arg1, arg2); + tree second_compare = fold_build3 (COND_EXPR, int_type_node, + ifexp2, integer_one_node, + integer_zero_node); + return fold_build3 (COND_EXPR, int_type_node, + ifexp1, integer_minus_one_node, second_compare); + } + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + if (TREE_CODE (type) == REAL_TYPE + && op == TRUNC_MOD_EXPR) + { + tree call; + if (type != double_type_node) + { + arg1 = convert (double_type_node, arg1); + arg2 = convert (double_type_node, arg2); + } + call = build_call_nary (double_type_node, + build_address_of (soft_fmod_node), + 2, arg1, arg2); + if (type != double_type_node) + call = convert (type, call); + return call; + } + + if (TREE_CODE (type) == INTEGER_TYPE + && flag_use_divide_subroutine + && ! flag_syntax_only) + return build_java_soft_divmod (op, type, arg1, arg2); + + break; + default: ; + } + return fold_build2 (op, type, arg1, arg2); +} + +static void +expand_java_binop (tree type, enum tree_code op) +{ + tree larg, rarg; + tree ltype = type; + tree rtype = type; + switch (op) + { + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case URSHIFT_EXPR: + rtype = int_type_node; + rarg = pop_value (rtype); + break; + default: + rarg = pop_value (rtype); + } + larg = pop_value (ltype); + push_value (build_java_binop (op, type, larg, rarg)); +} + +/* Lookup the field named NAME in *TYPEP or its super classes. + If not found, return NULL_TREE. + (If the *TYPEP is not found, or if the field reference is + ambiguous, return error_mark_node.) + If found, return the FIELD_DECL, and set *TYPEP to the + class containing the field. */ + +tree +lookup_field (tree *typep, tree name) +{ + if (CLASS_P (*typep) && !CLASS_LOADED_P (*typep)) + { + load_class (*typep, 1); + safe_layout_class (*typep); + if (!TYPE_SIZE (*typep) || TREE_CODE (TYPE_SIZE (*typep)) == ERROR_MARK) + return error_mark_node; + } + do + { + tree field, binfo, base_binfo; + tree save_field; + int i; + + for (field = TYPE_FIELDS (*typep); field; field = DECL_CHAIN (field)) + if (DECL_NAME (field) == name) + return field; + + /* Process implemented interfaces. */ + save_field = NULL_TREE; + for (binfo = TYPE_BINFO (*typep), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree t = BINFO_TYPE (base_binfo); + if ((field = lookup_field (&t, name))) + { + if (save_field == field) + continue; + if (save_field == NULL_TREE) + save_field = field; + else + { + tree i1 = DECL_CONTEXT (save_field); + tree i2 = DECL_CONTEXT (field); + error ("reference %qs is ambiguous: appears in interface %qs and interface %qs", + IDENTIFIER_POINTER (name), + IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (i1))), + IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (i2)))); + return error_mark_node; + } + } + } + + if (save_field != NULL_TREE) + return save_field; + + *typep = CLASSTYPE_SUPER (*typep); + } while (*typep); + return NULL_TREE; +} + +/* Look up the field named NAME in object SELF_VALUE, + which has class SELF_CLASS (a non-handle RECORD_TYPE). + SELF_VALUE is NULL_TREE if looking for a static field. */ + +tree +build_field_ref (tree self_value, tree self_class, tree name) +{ + tree base_class = self_class; + tree field_decl = lookup_field (&base_class, name); + if (field_decl == NULL_TREE) + { + error ("field %qs not found", IDENTIFIER_POINTER (name)); + return error_mark_node; + } + if (self_value == NULL_TREE) + { + return build_static_field_ref (field_decl); + } + else + { + tree base_type = promote_type (base_class); + + /* CHECK is true if self_value is not the this pointer. */ + int check = (! (DECL_P (self_value) + && DECL_NAME (self_value) == this_identifier_node)); + + /* Determine whether a field offset from NULL will lie within + Page 0: this is necessary on those GNU/Linux/BSD systems that + trap SEGV to generate NullPointerExceptions. + + We assume that Page 0 will be mapped with NOPERM, and that + memory may be allocated from any other page, so only field + offsets < pagesize are guaranteed to trap. We also assume + the smallest page size we'll encounter is 4k bytes. */ + if (! flag_syntax_only && check && ! flag_check_references + && ! flag_indirect_dispatch) + { + tree field_offset = byte_position (field_decl); + if (! page_size) + page_size = size_int (4096); + check = ! INT_CST_LT_UNSIGNED (field_offset, page_size); + } + + if (base_type != TREE_TYPE (self_value)) + self_value = fold_build1 (NOP_EXPR, base_type, self_value); + if (! flag_syntax_only && flag_indirect_dispatch) + { + tree otable_index + = build_int_cst (NULL_TREE, get_symbol_table_index + (field_decl, NULL_TREE, + &TYPE_OTABLE_METHODS (output_class))); + tree field_offset + = build4 (ARRAY_REF, integer_type_node, + TYPE_OTABLE_DECL (output_class), otable_index, + NULL_TREE, NULL_TREE); + tree address; + + if (DECL_CONTEXT (field_decl) != output_class) + field_offset + = build3 (COND_EXPR, TREE_TYPE (field_offset), + build2 (EQ_EXPR, boolean_type_node, + field_offset, integer_zero_node), + build_call_nary (void_type_node, + build_address_of (soft_nosuchfield_node), + 1, otable_index), + field_offset); + + field_offset = fold (convert (sizetype, field_offset)); + self_value = java_check_reference (self_value, check); + address + = fold_build2 (POINTER_PLUS_EXPR, + TREE_TYPE (self_value), + self_value, field_offset); + address = fold_convert (build_pointer_type (TREE_TYPE (field_decl)), + address); + return fold_build1 (INDIRECT_REF, TREE_TYPE (field_decl), address); + } + + self_value = build_java_indirect_ref (TREE_TYPE (TREE_TYPE (self_value)), + self_value, check); + return fold_build3 (COMPONENT_REF, TREE_TYPE (field_decl), + self_value, field_decl, NULL_TREE); + } +} + +tree +lookup_label (int pc) +{ + tree name; + char buf[32]; + if (pc > highest_label_pc_this_method) + highest_label_pc_this_method = pc; + targetm.asm_out.generate_internal_label (buf, "LJpc=", + start_label_pc_this_method + pc); + name = get_identifier (buf); + if (IDENTIFIER_LOCAL_VALUE (name)) + return IDENTIFIER_LOCAL_VALUE (name); + else + { + /* The type of the address of a label is return_address_type_node. */ + tree decl = create_label_decl (name); + return pushdecl (decl); + } +} + +/* Generate a unique name for the purpose of loops and switches + labels, and try-catch-finally blocks label or temporary variables. */ + +tree +generate_name (void) +{ + static int l_number = 0; + char buff [32]; + targetm.asm_out.generate_internal_label (buff, "LJv", l_number); + l_number++; + return get_identifier (buff); +} + +tree +create_label_decl (tree name) +{ + tree decl; + decl = build_decl (input_location, LABEL_DECL, name, + TREE_TYPE (return_address_type_node)); + DECL_CONTEXT (decl) = current_function_decl; + DECL_IGNORED_P (decl) = 1; + return decl; +} + +/* This maps a bytecode offset (PC) to various flags. */ +char *instruction_bits; + +/* This is a vector of type states for the current method. It is + indexed by PC. Each element is a tree vector holding the type + state at that PC. We only note type states at basic block + boundaries. */ +VEC(tree, gc) *type_states; + +static void +note_label (int current_pc ATTRIBUTE_UNUSED, int target_pc) +{ + lookup_label (target_pc); + instruction_bits [target_pc] |= BCODE_JUMP_TARGET; +} + +/* Emit code to jump to TARGET_PC if VALUE1 CONDITION VALUE2, + where CONDITION is one of one the compare operators. */ + +static void +expand_compare (enum tree_code condition, tree value1, tree value2, + int target_pc) +{ + tree target = lookup_label (target_pc); + tree cond = fold_build2 (condition, boolean_type_node, value1, value2); + java_add_stmt + (build3 (COND_EXPR, void_type_node, java_truthvalue_conversion (cond), + build1 (GOTO_EXPR, void_type_node, target), + build_java_empty_stmt ())); +} + +/* Emit code for a TEST-type opcode. */ + +static void +expand_test (enum tree_code condition, tree type, int target_pc) +{ + tree value1, value2; + flush_quick_stack (); + value1 = pop_value (type); + value2 = (type == ptr_type_node) ? null_pointer_node : integer_zero_node; + expand_compare (condition, value1, value2, target_pc); +} + +/* Emit code for a COND-type opcode. */ + +static void +expand_cond (enum tree_code condition, tree type, int target_pc) +{ + tree value1, value2; + flush_quick_stack (); + /* note: pop values in opposite order */ + value2 = pop_value (type); + value1 = pop_value (type); + /* Maybe should check value1 and value2 for type compatibility ??? */ + expand_compare (condition, value1, value2, target_pc); +} + +static void +expand_java_goto (int target_pc) +{ + tree target_label = lookup_label (target_pc); + flush_quick_stack (); + java_add_stmt (build1 (GOTO_EXPR, void_type_node, target_label)); +} + +static tree +expand_java_switch (tree selector, int default_pc) +{ + tree switch_expr, x; + + flush_quick_stack (); + switch_expr = build3 (SWITCH_EXPR, TREE_TYPE (selector), selector, + NULL_TREE, NULL_TREE); + java_add_stmt (switch_expr); + + x = build3 (CASE_LABEL_EXPR, void_type_node, NULL_TREE, NULL_TREE, + create_artificial_label (input_location)); + append_to_statement_list (x, &SWITCH_BODY (switch_expr)); + + x = build1 (GOTO_EXPR, void_type_node, lookup_label (default_pc)); + append_to_statement_list (x, &SWITCH_BODY (switch_expr)); + + return switch_expr; +} + +static void +expand_java_add_case (tree switch_expr, int match, int target_pc) +{ + tree value, x; + + value = build_int_cst (TREE_TYPE (switch_expr), match); + + x = build3 (CASE_LABEL_EXPR, void_type_node, value, NULL_TREE, + create_artificial_label (input_location)); + append_to_statement_list (x, &SWITCH_BODY (switch_expr)); + + x = build1 (GOTO_EXPR, void_type_node, lookup_label (target_pc)); + append_to_statement_list (x, &SWITCH_BODY (switch_expr)); +} + +static VEC(tree,gc) * +pop_arguments (tree method_type) +{ + function_args_iterator fnai; + tree type; + VEC(tree,gc) *args = NULL; + int arity; + + FOREACH_FUNCTION_ARGS (method_type, type, fnai) + { + /* XXX: leaky abstraction. */ + if (type == void_type_node) + break; + + VEC_safe_push (tree, gc, args, type); + } + + arity = VEC_length (tree, args); + + while (arity--) + { + tree arg = pop_value (VEC_index (tree, args, arity)); + + /* We simply cast each argument to its proper type. This is + needed since we lose type information coming out of the + verifier. We also have to do this when we pop an integer + type that must be promoted for the function call. */ + if (TREE_CODE (type) == POINTER_TYPE) + arg = build1 (NOP_EXPR, type, arg); + else if (targetm.calls.promote_prototypes (type) + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) + && INTEGRAL_TYPE_P (type)) + arg = convert (integer_type_node, arg); + + VEC_replace (tree, args, arity, arg); + } + + return args; +} + +/* Attach to PTR (a block) the declaration found in ENTRY. */ + +int +attach_init_test_initialization_flags (void **entry, void *ptr) +{ + tree block = (tree)ptr; + struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry; + + if (block != error_mark_node) + { + if (TREE_CODE (block) == BIND_EXPR) + { + tree body = BIND_EXPR_BODY (block); + DECL_CHAIN (ite->value) = BIND_EXPR_VARS (block); + BIND_EXPR_VARS (block) = ite->value; + body = build2 (COMPOUND_EXPR, void_type_node, + build1 (DECL_EXPR, void_type_node, ite->value), body); + BIND_EXPR_BODY (block) = body; + } + else + { + tree body = BLOCK_SUBBLOCKS (block); + TREE_CHAIN (ite->value) = BLOCK_EXPR_DECLS (block); + BLOCK_EXPR_DECLS (block) = ite->value; + body = build2 (COMPOUND_EXPR, void_type_node, + build1 (DECL_EXPR, void_type_node, ite->value), body); + BLOCK_SUBBLOCKS (block) = body; + } + + } + return true; +} + +/* Build an expression to initialize the class CLAS. + if EXPR is non-NULL, returns an expression to first call the initializer + (if it is needed) and then calls EXPR. */ + +tree +build_class_init (tree clas, tree expr) +{ + tree init; + + /* An optimization: if CLAS is a superclass of the class we're + compiling, we don't need to initialize it. However, if CLAS is + an interface, it won't necessarily be initialized, even if we + implement it. */ + if ((! CLASS_INTERFACE (TYPE_NAME (clas)) + && inherits_from_p (current_class, clas)) + || current_class == clas) + return expr; + + if (always_initialize_class_p) + { + init = build_call_nary (void_type_node, + build_address_of (soft_initclass_node), + 1, build_class_ref (clas)); + TREE_SIDE_EFFECTS (init) = 1; + } + else + { + tree *init_test_decl; + tree decl; + init_test_decl = java_treetreehash_new + (DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), clas); + + if (*init_test_decl == NULL) + { + /* Build a declaration and mark it as a flag used to track + static class initializations. */ + decl = build_decl (input_location, VAR_DECL, NULL_TREE, + boolean_type_node); + MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl); + DECL_CONTEXT (decl) = current_function_decl; + DECL_INITIAL (decl) = boolean_false_node; + /* Don't emit any symbolic debugging info for this decl. */ + DECL_IGNORED_P (decl) = 1; + *init_test_decl = decl; + } + + init = build_call_nary (void_type_node, + build_address_of (soft_initclass_node), + 1, build_class_ref (clas)); + TREE_SIDE_EFFECTS (init) = 1; + init = build3 (COND_EXPR, void_type_node, + build2 (EQ_EXPR, boolean_type_node, + *init_test_decl, boolean_false_node), + init, integer_zero_node); + TREE_SIDE_EFFECTS (init) = 1; + init = build2 (COMPOUND_EXPR, TREE_TYPE (expr), init, + build2 (MODIFY_EXPR, boolean_type_node, + *init_test_decl, boolean_true_node)); + TREE_SIDE_EFFECTS (init) = 1; + } + + if (expr != NULL_TREE) + { + expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), init, expr); + TREE_SIDE_EFFECTS (expr) = 1; + return expr; + } + return init; +} + + + +/* Rewrite expensive calls that require stack unwinding at runtime to + cheaper alternatives. The logic here performs these + transformations: + + java.lang.Class.forName("foo") -> java.lang.Class.forName("foo", class$) + java.lang.Class.getClassLoader() -> java.lang.Class.getClassLoader(class$) + +*/ + +typedef struct +{ + const char *classname; + const char *method; + const char *signature; + const char *new_classname; + const char *new_signature; + int flags; + void (*rewrite_arglist) (VEC(tree,gc) **); +} rewrite_rule; + +/* Add __builtin_return_address(0) to the end of an arglist. */ + + +static void +rewrite_arglist_getcaller (VEC(tree,gc) **arglist) +{ + tree retaddr + = build_call_expr (built_in_decls[BUILT_IN_RETURN_ADDRESS], + 1, integer_zero_node); + + DECL_UNINLINABLE (current_function_decl) = 1; + + VEC_safe_push (tree, gc, *arglist, retaddr); +} + +/* Add this.class to the end of an arglist. */ + +static void +rewrite_arglist_getclass (VEC(tree,gc) **arglist) +{ + VEC_safe_push (tree, gc, *arglist, build_class_ref (output_class)); +} + +static rewrite_rule rules[] = + {{"java.lang.Class", "getClassLoader", "()Ljava/lang/ClassLoader;", + "java.lang.Class", "(Ljava/lang/Class;)Ljava/lang/ClassLoader;", + ACC_FINAL|ACC_PRIVATE, rewrite_arglist_getclass}, + + {"java.lang.Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", + "java.lang.Class", "(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Class;", + ACC_FINAL|ACC_PRIVATE|ACC_STATIC, rewrite_arglist_getclass}, + + {"gnu.classpath.VMStackWalker", "getCallingClass", "()Ljava/lang/Class;", + "gnu.classpath.VMStackWalker", "(Lgnu/gcj/RawData;)Ljava/lang/Class;", + ACC_FINAL|ACC_PRIVATE|ACC_STATIC, rewrite_arglist_getcaller}, + + {"gnu.classpath.VMStackWalker", "getCallingClassLoader", + "()Ljava/lang/ClassLoader;", + "gnu.classpath.VMStackWalker", "(Lgnu/gcj/RawData;)Ljava/lang/ClassLoader;", + ACC_FINAL|ACC_PRIVATE|ACC_STATIC, rewrite_arglist_getcaller}, + + {"gnu.java.lang.VMCPStringBuilder", "toString", "([CII)Ljava/lang/String;", + "java.lang.String", "([CII)Ljava/lang/String;", + ACC_FINAL|ACC_PRIVATE|ACC_STATIC, NULL}, + + {NULL, NULL, NULL, NULL, NULL, 0, NULL}}; + +/* True if this method is special, i.e. it's a private method that + should be exported from a DSO. */ + +bool +special_method_p (tree candidate_method) +{ + tree context = DECL_NAME (TYPE_NAME (DECL_CONTEXT (candidate_method))); + tree method = DECL_NAME (candidate_method); + rewrite_rule *p; + + for (p = rules; p->classname; p++) + { + if (get_identifier (p->classname) == context + && get_identifier (p->method) == method) + return true; + } + return false; +} + +/* Scan the rules list for replacements for *METHOD_P and replace the + args accordingly. If the rewrite results in an access to a private + method, update SPECIAL.*/ + +void +maybe_rewrite_invocation (tree *method_p, VEC(tree,gc) **arg_list_p, + tree *method_signature_p, tree *special) +{ + tree context = DECL_NAME (TYPE_NAME (DECL_CONTEXT (*method_p))); + rewrite_rule *p; + *special = NULL_TREE; + + for (p = rules; p->classname; p++) + { + if (get_identifier (p->classname) == context) + { + tree method = DECL_NAME (*method_p); + if (get_identifier (p->method) == method + && get_identifier (p->signature) == *method_signature_p) + { + tree maybe_method; + tree destination_class + = lookup_class (get_identifier (p->new_classname)); + gcc_assert (destination_class); + maybe_method + = lookup_java_method (destination_class, + method, + get_identifier (p->new_signature)); + if (! maybe_method && ! flag_verify_invocations) + { + maybe_method + = add_method (destination_class, p->flags, + method, get_identifier (p->new_signature)); + DECL_EXTERNAL (maybe_method) = 1; + } + *method_p = maybe_method; + gcc_assert (*method_p); + if (p->rewrite_arglist) + p->rewrite_arglist (arg_list_p); + *method_signature_p = get_identifier (p->new_signature); + *special = integer_one_node; + + break; + } + } + } +} + + + +tree +build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED, + tree self_type, tree method_signature ATTRIBUTE_UNUSED, + VEC(tree,gc) *arg_list ATTRIBUTE_UNUSED, tree special) +{ + tree func; + if (is_compiled_class (self_type)) + { + /* With indirect dispatch we have to use indirect calls for all + publicly visible methods or gcc will use PLT indirections + to reach them. We also have to use indirect dispatch for all + external methods. */ + if (! flag_indirect_dispatch + || (! DECL_EXTERNAL (method) && ! TREE_PUBLIC (method))) + { + func = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (method)), + method); + } + else + { + tree table_index + = build_int_cst (NULL_TREE, + (get_symbol_table_index + (method, special, + &TYPE_ATABLE_METHODS (output_class)))); + func + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ATABLE_DECL (output_class))), + TYPE_ATABLE_DECL (output_class), table_index, + NULL_TREE, NULL_TREE); + } + func = convert (method_ptr_type_node, func); + } + else + { + /* We don't know whether the method has been (statically) compiled. + Compile this code to get a reference to the method's code: + + SELF_TYPE->methods[METHOD_INDEX].ncode + + */ + + int method_index = 0; + tree meth, ref; + + /* The method might actually be declared in some superclass, so + we have to use its class context, not the caller's notion of + where the method is. */ + self_type = DECL_CONTEXT (method); + ref = build_class_ref (self_type); + ref = build1 (INDIRECT_REF, class_type_node, ref); + if (ncode_ident == NULL_TREE) + ncode_ident = get_identifier ("ncode"); + if (methods_ident == NULL_TREE) + methods_ident = get_identifier ("methods"); + ref = build3 (COMPONENT_REF, method_ptr_type_node, ref, + lookup_field (&class_type_node, methods_ident), + NULL_TREE); + for (meth = TYPE_METHODS (self_type); + ; meth = DECL_CHAIN (meth)) + { + if (method == meth) + break; + if (meth == NULL_TREE) + fatal_error ("method '%s' not found in class", + IDENTIFIER_POINTER (DECL_NAME (method))); + method_index++; + } + method_index *= int_size_in_bytes (method_type_node); + ref = fold_build2 (POINTER_PLUS_EXPR, method_ptr_type_node, + ref, size_int (method_index)); + ref = build1 (INDIRECT_REF, method_type_node, ref); + func = build3 (COMPONENT_REF, nativecode_ptr_type_node, + ref, lookup_field (&method_type_node, ncode_ident), + NULL_TREE); + } + return func; +} + +tree +invoke_build_dtable (int is_invoke_interface, VEC(tree,gc) *arg_list) +{ + tree dtable, objectref; + tree saved = save_expr (VEC_index (tree, arg_list, 0)); + + VEC_replace (tree, arg_list, 0, saved); + + /* If we're dealing with interfaces and if the objectref + argument is an array then get the dispatch table of the class + Object rather than the one from the objectref. */ + objectref = (is_invoke_interface + && is_array_type_p (TREE_TYPE (saved)) + ? build_class_ref (object_type_node) : saved); + + if (dtable_ident == NULL_TREE) + dtable_ident = get_identifier ("vtable"); + dtable = build_java_indirect_ref (object_type_node, objectref, + flag_check_references); + dtable = build3 (COMPONENT_REF, dtable_ptr_type, dtable, + lookup_field (&object_type_node, dtable_ident), NULL_TREE); + + return dtable; +} + +/* Determine the index in SYMBOL_TABLE for a reference to the decl + T. If this decl has not been seen before, it will be added to the + [oa]table_methods. If it has, the existing table slot will be + reused. */ + +int +get_symbol_table_index (tree t, tree special, + VEC(method_entry,gc) **symbol_table) +{ + method_entry *e; + unsigned i; + + FOR_EACH_VEC_ELT (method_entry, *symbol_table, i, e) + if (t == e->method && special == e->special) + goto done; + + e = VEC_safe_push (method_entry, gc, *symbol_table, NULL); + e->method = t; + e->special = special; + + done: + return i + 1; +} + +tree +build_invokevirtual (tree dtable, tree method, tree special) +{ + tree func; + tree nativecode_ptr_ptr_type_node + = build_pointer_type (nativecode_ptr_type_node); + tree method_index; + tree otable_index; + + if (flag_indirect_dispatch) + { + gcc_assert (! CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method)))); + + otable_index + = build_int_cst (NULL_TREE, get_symbol_table_index + (method, special, + &TYPE_OTABLE_METHODS (output_class))); + method_index = build4 (ARRAY_REF, integer_type_node, + TYPE_OTABLE_DECL (output_class), + otable_index, NULL_TREE, NULL_TREE); + } + else + { + /* We fetch the DECL_VINDEX field directly here, rather than + using get_method_index(). DECL_VINDEX is the true offset + from the vtable base to a method, regrdless of any extra + words inserted at the start of the vtable. */ + method_index = DECL_VINDEX (method); + method_index = size_binop (MULT_EXPR, method_index, + TYPE_SIZE_UNIT (nativecode_ptr_ptr_type_node)); + if (TARGET_VTABLE_USES_DESCRIPTORS) + method_index = size_binop (MULT_EXPR, method_index, + size_int (TARGET_VTABLE_USES_DESCRIPTORS)); + } + + func = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (dtable), dtable, + convert (sizetype, method_index)); + + if (TARGET_VTABLE_USES_DESCRIPTORS) + func = build1 (NOP_EXPR, nativecode_ptr_type_node, func); + else + { + func = fold_convert (nativecode_ptr_ptr_type_node, func); + func = build1 (INDIRECT_REF, nativecode_ptr_type_node, func); + } + + return func; +} + +static GTY(()) tree class_ident; +tree +build_invokeinterface (tree dtable, tree method) +{ + tree interface; + tree idx; + + /* We expand invokeinterface here. */ + + if (class_ident == NULL_TREE) + class_ident = get_identifier ("class"); + + dtable = build_java_indirect_ref (dtable_type, dtable, + flag_check_references); + dtable = build3 (COMPONENT_REF, class_ptr_type, dtable, + lookup_field (&dtable_type, class_ident), NULL_TREE); + + interface = DECL_CONTEXT (method); + gcc_assert (CLASS_INTERFACE (TYPE_NAME (interface))); + layout_class_methods (interface); + + if (flag_indirect_dispatch) + { + int itable_index + = 2 * (get_symbol_table_index + (method, NULL_TREE, &TYPE_ITABLE_METHODS (output_class))); + interface + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))), + TYPE_ITABLE_DECL (output_class), + build_int_cst (NULL_TREE, itable_index-1), + NULL_TREE, NULL_TREE); + idx + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))), + TYPE_ITABLE_DECL (output_class), + build_int_cst (NULL_TREE, itable_index), + NULL_TREE, NULL_TREE); + interface = convert (class_ptr_type, interface); + idx = convert (integer_type_node, idx); + } + else + { + idx = build_int_cst (NULL_TREE, + get_interface_method_index (method, interface)); + interface = build_class_ref (interface); + } + + return build_call_nary (ptr_type_node, + build_address_of (soft_lookupinterfacemethod_node), + 3, dtable, interface, idx); +} + +/* Expand one of the invoke_* opcodes. + OPCODE is the specific opcode. + METHOD_REF_INDEX is an index into the constant pool. + NARGS is the number of arguments, or -1 if not specified. */ + +static void +expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED) +{ + tree method_signature + = COMPONENT_REF_SIGNATURE(¤t_jcf->cpool, method_ref_index); + tree method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, + method_ref_index); + tree self_type + = get_class_constant (current_jcf, + COMPONENT_REF_CLASS_INDEX(¤t_jcf->cpool, + method_ref_index)); + const char *const self_name + = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type))); + tree call, func, method, method_type; + VEC(tree,gc) *arg_list; + tree check = NULL_TREE; + + tree special = NULL_TREE; + + if (! CLASS_LOADED_P (self_type)) + { + load_class (self_type, 1); + safe_layout_class (self_type); + if (TREE_CODE (TYPE_SIZE (self_type)) == ERROR_MARK) + fatal_error ("failed to find class '%s'", self_name); + } + layout_class_methods (self_type); + + if (ID_INIT_P (method_name)) + method = lookup_java_constructor (self_type, method_signature); + else + method = lookup_java_method (self_type, method_name, method_signature); + + /* We've found a method in a class other than the one in which it + was wanted. This can happen if, for instance, we're trying to + compile invokespecial super.equals(). + FIXME: This is a kludge. Rather than nullifying the result, we + should change lookup_java_method() so that it doesn't search the + superclass chain when we're BC-compiling. */ + if (! flag_verify_invocations + && method + && ! TYPE_ARRAY_P (self_type) + && self_type != DECL_CONTEXT (method)) + method = NULL_TREE; + + /* We've found a method in an interface, but this isn't an interface + call. */ + if (opcode != OPCODE_invokeinterface + && method + && (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method))))) + method = NULL_TREE; + + /* We've found a non-interface method but we are making an + interface call. This can happen if the interface overrides a + method in Object. */ + if (! flag_verify_invocations + && opcode == OPCODE_invokeinterface + && method + && ! CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method)))) + method = NULL_TREE; + + if (method == NULL_TREE) + { + if (flag_verify_invocations || ! flag_indirect_dispatch) + { + error ("class '%s' has no method named '%s' matching signature '%s'", + self_name, + IDENTIFIER_POINTER (method_name), + IDENTIFIER_POINTER (method_signature)); + } + else + { + int flags = ACC_PUBLIC; + if (opcode == OPCODE_invokestatic) + flags |= ACC_STATIC; + if (opcode == OPCODE_invokeinterface) + { + flags |= ACC_INTERFACE | ACC_ABSTRACT; + CLASS_INTERFACE (TYPE_NAME (self_type)) = 1; + } + method = add_method (self_type, flags, method_name, + method_signature); + DECL_ARTIFICIAL (method) = 1; + METHOD_DUMMY (method) = 1; + layout_class_method (self_type, NULL, + method, NULL); + } + } + + /* Invoke static can't invoke static/abstract method */ + if (method != NULL_TREE) + { + if (opcode == OPCODE_invokestatic) + { + if (!METHOD_STATIC (method)) + { + error ("invokestatic on non static method"); + method = NULL_TREE; + } + else if (METHOD_ABSTRACT (method)) + { + error ("invokestatic on abstract method"); + method = NULL_TREE; + } + } + else + { + if (METHOD_STATIC (method)) + { + error ("invoke[non-static] on static method"); + method = NULL_TREE; + } + } + } + + if (method == NULL_TREE) + { + /* If we got here, we emitted an error message above. So we + just pop the arguments, push a properly-typed zero, and + continue. */ + method_type = get_type_from_signature (method_signature); + pop_arguments (method_type); + if (opcode != OPCODE_invokestatic) + pop_type (self_type); + method_type = promote_type (TREE_TYPE (method_type)); + push_value (convert (method_type, integer_zero_node)); + return; + } + + method_type = TREE_TYPE (method); + arg_list = pop_arguments (method_type); + flush_quick_stack (); + + maybe_rewrite_invocation (&method, &arg_list, &method_signature, + &special); + + func = NULL_TREE; + if (opcode == OPCODE_invokestatic) + func = build_known_method_ref (method, method_type, self_type, + method_signature, arg_list, special); + else if (opcode == OPCODE_invokespecial + || (opcode == OPCODE_invokevirtual + && (METHOD_PRIVATE (method) + || METHOD_FINAL (method) + || CLASS_FINAL (TYPE_NAME (self_type))))) + { + /* If the object for the method call is null, we throw an + exception. We don't do this if the object is the current + method's `this'. In other cases we just rely on an + optimization pass to eliminate redundant checks. FIXME: + Unfortunately there doesn't seem to be a way to determine + what the current method is right now. + We do omit the check if we're calling <init>. */ + /* We use a SAVE_EXPR here to make sure we only evaluate + the new `self' expression once. */ + tree save_arg = save_expr (VEC_index (tree, arg_list, 0)); + VEC_replace (tree, arg_list, 0, save_arg); + check = java_check_reference (save_arg, ! DECL_INIT_P (method)); + func = build_known_method_ref (method, method_type, self_type, + method_signature, arg_list, special); + } + else + { + tree dtable = invoke_build_dtable (opcode == OPCODE_invokeinterface, + arg_list); + if (opcode == OPCODE_invokevirtual) + func = build_invokevirtual (dtable, method, special); + else + func = build_invokeinterface (dtable, method); + } + + if (TREE_CODE (func) == ADDR_EXPR) + TREE_TYPE (func) = build_pointer_type (method_type); + else + func = build1 (NOP_EXPR, build_pointer_type (method_type), func); + + call = build_call_vec (TREE_TYPE (method_type), func, arg_list); + TREE_SIDE_EFFECTS (call) = 1; + call = check_for_builtin (method, call); + + if (check != NULL_TREE) + { + call = build2 (COMPOUND_EXPR, TREE_TYPE (call), check, call); + TREE_SIDE_EFFECTS (call) = 1; + } + + if (TREE_CODE (TREE_TYPE (method_type)) == VOID_TYPE) + java_add_stmt (call); + else + { + push_value (call); + flush_quick_stack (); + } +} + +/* Create a stub which will be put into the vtable but which will call + a JNI function. */ + +tree +build_jni_stub (tree method) +{ + tree jnifunc, call, body, method_sig, arg_types; + tree jniarg0, jniarg1, jniarg2, jniarg3; + tree jni_func_type, tem; + tree env_var, res_var = NULL_TREE, block; + tree method_args; + tree meth_var; + tree bind; + VEC(tree,gc) *args = NULL; + int args_size = 0; + + tree klass = DECL_CONTEXT (method); + klass = build_class_ref (klass); + + gcc_assert (METHOD_NATIVE (method) && flag_jni); + + DECL_ARTIFICIAL (method) = 1; + DECL_EXTERNAL (method) = 0; + + env_var = build_decl (input_location, + VAR_DECL, get_identifier ("env"), ptr_type_node); + DECL_CONTEXT (env_var) = method; + + if (TREE_TYPE (TREE_TYPE (method)) != void_type_node) + { + res_var = build_decl (input_location, VAR_DECL, get_identifier ("res"), + TREE_TYPE (TREE_TYPE (method))); + DECL_CONTEXT (res_var) = method; + DECL_CHAIN (env_var) = res_var; + } + + method_args = DECL_ARGUMENTS (method); + block = build_block (env_var, NULL_TREE, method_args, NULL_TREE); + TREE_SIDE_EFFECTS (block) = 1; + TREE_TYPE (block) = TREE_TYPE (TREE_TYPE (method)); + + /* Compute the local `env' by calling _Jv_GetJNIEnvNewFrame. */ + body = build2 (MODIFY_EXPR, ptr_type_node, env_var, + build_call_nary (ptr_type_node, + build_address_of (soft_getjnienvnewframe_node), + 1, klass)); + + /* The JNIEnv structure is the first argument to the JNI function. */ + args_size += int_size_in_bytes (TREE_TYPE (env_var)); + VEC_safe_push (tree, gc, args, env_var); + + /* For a static method the second argument is the class. For a + non-static method the second argument is `this'; that is already + available in the argument list. */ + if (METHOD_STATIC (method)) + { + args_size += int_size_in_bytes (TREE_TYPE (klass)); + VEC_safe_push (tree, gc, args, klass); + } + + /* All the arguments to this method become arguments to the + underlying JNI function. If we had to wrap object arguments in a + special way, we would do that here. */ + for (tem = method_args; tem != NULL_TREE; tem = DECL_CHAIN (tem)) + { + int arg_bits = TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tem))); +#ifdef PARM_BOUNDARY + arg_bits = (((arg_bits + PARM_BOUNDARY - 1) / PARM_BOUNDARY) + * PARM_BOUNDARY); +#endif + args_size += (arg_bits / BITS_PER_UNIT); + + VEC_safe_push (tree, gc, args, tem); + } + arg_types = TYPE_ARG_TYPES (TREE_TYPE (method)); + + /* Argument types for static methods and the JNIEnv structure. + FIXME: Write and use build_function_type_vec to avoid this. */ + if (METHOD_STATIC (method)) + arg_types = tree_cons (NULL_TREE, object_ptr_type_node, arg_types); + arg_types = tree_cons (NULL_TREE, ptr_type_node, arg_types); + + /* We call _Jv_LookupJNIMethod to find the actual underlying + function pointer. _Jv_LookupJNIMethod will throw the appropriate + exception if this function is not found at runtime. */ + method_sig = build_java_signature (TREE_TYPE (method)); + jniarg0 = klass; + jniarg1 = build_utf8_ref (DECL_NAME (method)); + jniarg2 = build_utf8_ref (unmangle_classname + (IDENTIFIER_POINTER (method_sig), + IDENTIFIER_LENGTH (method_sig))); + jniarg3 = build_int_cst (NULL_TREE, args_size); + + tem = build_function_type (TREE_TYPE (TREE_TYPE (method)), arg_types); + +#ifdef MODIFY_JNI_METHOD_CALL + tem = MODIFY_JNI_METHOD_CALL (tem); +#endif + + jni_func_type = build_pointer_type (tem); + + /* Use the actual function type, rather than a generic pointer type, + such that this decl keeps the actual pointer type from being + garbage-collected. If it is, we end up using canonical types + with different uids for equivalent function types, and this in + turn causes utf8 identifiers and output order to vary. */ + meth_var = build_decl (input_location, + VAR_DECL, get_identifier ("meth"), jni_func_type); + TREE_STATIC (meth_var) = 1; + TREE_PUBLIC (meth_var) = 0; + DECL_EXTERNAL (meth_var) = 0; + DECL_CONTEXT (meth_var) = method; + DECL_ARTIFICIAL (meth_var) = 1; + DECL_INITIAL (meth_var) = null_pointer_node; + TREE_USED (meth_var) = 1; + chainon (env_var, meth_var); + build_result_decl (method); + + jnifunc = build3 (COND_EXPR, jni_func_type, + build2 (NE_EXPR, boolean_type_node, + meth_var, build_int_cst (TREE_TYPE (meth_var), 0)), + meth_var, + build2 (MODIFY_EXPR, jni_func_type, meth_var, + build1 + (NOP_EXPR, jni_func_type, + build_call_nary (ptr_type_node, + build_address_of + (soft_lookupjnimethod_node), + 4, + jniarg0, jniarg1, + jniarg2, jniarg3)))); + + /* Now we make the actual JNI call via the resulting function + pointer. */ + call = build_call_vec (TREE_TYPE (TREE_TYPE (method)), jnifunc, args); + + /* If the JNI call returned a result, capture it here. If we had to + unwrap JNI object results, we would do that here. */ + if (res_var != NULL_TREE) + { + /* If the call returns an object, it may return a JNI weak + reference, in which case we must unwrap it. */ + if (! JPRIMITIVE_TYPE_P (TREE_TYPE (TREE_TYPE (method)))) + call = build_call_nary (TREE_TYPE (TREE_TYPE (method)), + build_address_of (soft_unwrapjni_node), + 1, call); + call = build2 (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (method)), + res_var, call); + } + + TREE_SIDE_EFFECTS (call) = 1; + + body = build2 (COMPOUND_EXPR, void_type_node, body, call); + TREE_SIDE_EFFECTS (body) = 1; + + /* Now free the environment we allocated. */ + call = build_call_nary (ptr_type_node, + build_address_of (soft_jnipopsystemframe_node), + 1, env_var); + TREE_SIDE_EFFECTS (call) = 1; + body = build2 (COMPOUND_EXPR, void_type_node, body, call); + TREE_SIDE_EFFECTS (body) = 1; + + /* Finally, do the return. */ + if (res_var != NULL_TREE) + { + tree drt; + gcc_assert (DECL_RESULT (method)); + /* Make sure we copy the result variable to the actual + result. We use the type of the DECL_RESULT because it + might be different from the return type of the function: + it might be promoted. */ + drt = TREE_TYPE (DECL_RESULT (method)); + if (drt != TREE_TYPE (res_var)) + res_var = build1 (CONVERT_EXPR, drt, res_var); + res_var = build2 (MODIFY_EXPR, drt, DECL_RESULT (method), res_var); + TREE_SIDE_EFFECTS (res_var) = 1; + } + + body = build2 (COMPOUND_EXPR, void_type_node, body, + build1 (RETURN_EXPR, void_type_node, res_var)); + TREE_SIDE_EFFECTS (body) = 1; + + /* Prepend class initialization for static methods reachable from + other classes. */ + if (METHOD_STATIC (method) + && (! METHOD_PRIVATE (method) + || INNER_CLASS_P (DECL_CONTEXT (method)))) + { + tree init = build_call_expr (soft_initclass_node, 1, + klass); + body = build2 (COMPOUND_EXPR, void_type_node, init, body); + TREE_SIDE_EFFECTS (body) = 1; + } + + bind = build3 (BIND_EXPR, void_type_node, BLOCK_VARS (block), + body, block); + return bind; +} + + +/* Given lvalue EXP, return a volatile expression that references the + same object. */ + +tree +java_modify_addr_for_volatile (tree exp) +{ + tree exp_type = TREE_TYPE (exp); + tree v_type + = build_qualified_type (exp_type, + TYPE_QUALS (exp_type) | TYPE_QUAL_VOLATILE); + tree addr = build_fold_addr_expr (exp); + v_type = build_pointer_type (v_type); + addr = fold_convert (v_type, addr); + exp = build_fold_indirect_ref (addr); + return exp; +} + + +/* Expand an operation to extract from or store into a field. + IS_STATIC is 1 iff the field is static. + IS_PUTTING is 1 for putting into a field; 0 for getting from the field. + FIELD_REF_INDEX is an index into the constant pool. */ + +static void +expand_java_field_op (int is_static, int is_putting, int field_ref_index) +{ + tree self_type + = get_class_constant (current_jcf, + COMPONENT_REF_CLASS_INDEX (¤t_jcf->cpool, + field_ref_index)); + const char *self_name + = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type))); + tree field_name = COMPONENT_REF_NAME (¤t_jcf->cpool, field_ref_index); + tree field_signature = COMPONENT_REF_SIGNATURE (¤t_jcf->cpool, + field_ref_index); + tree field_type = get_type_from_signature (field_signature); + tree new_value = is_putting ? pop_value (field_type) : NULL_TREE; + tree field_ref; + int is_error = 0; + tree original_self_type = self_type; + tree field_decl; + tree modify_expr; + + if (! CLASS_LOADED_P (self_type)) + load_class (self_type, 1); + field_decl = lookup_field (&self_type, field_name); + if (field_decl == error_mark_node) + { + is_error = 1; + } + else if (field_decl == NULL_TREE) + { + if (! flag_verify_invocations) + { + int flags = ACC_PUBLIC; + if (is_static) + flags |= ACC_STATIC; + self_type = original_self_type; + field_decl = add_field (original_self_type, field_name, + field_type, flags); + DECL_ARTIFICIAL (field_decl) = 1; + DECL_IGNORED_P (field_decl) = 1; +#if 0 + /* FIXME: We should be pessimistic about volatility. We + don't know one way or another, but this is safe. + However, doing this has bad effects on code quality. We + need to look at better ways to do this. */ + TREE_THIS_VOLATILE (field_decl) = 1; +#endif + } + else + { + error ("missing field '%s' in '%s'", + IDENTIFIER_POINTER (field_name), self_name); + is_error = 1; + } + } + else if (build_java_signature (TREE_TYPE (field_decl)) != field_signature) + { + error ("mismatching signature for field '%s' in '%s'", + IDENTIFIER_POINTER (field_name), self_name); + is_error = 1; + } + field_ref = is_static ? NULL_TREE : pop_value (self_type); + if (is_error) + { + if (! is_putting) + push_value (convert (field_type, integer_zero_node)); + flush_quick_stack (); + return; + } + + field_ref = build_field_ref (field_ref, self_type, field_name); + if (is_static + && ! flag_indirect_dispatch) + { + tree context = DECL_CONTEXT (field_ref); + if (context != self_type && CLASS_INTERFACE (TYPE_NAME (context))) + field_ref = build_class_init (context, field_ref); + else + field_ref = build_class_init (self_type, field_ref); + } + if (is_putting) + { + flush_quick_stack (); + if (FIELD_FINAL (field_decl)) + { + if (DECL_CONTEXT (field_decl) != current_class) + error ("assignment to final field %q+D not in field%'s class", + field_decl); + /* We used to check for assignments to final fields not + occurring in the class initializer or in a constructor + here. However, this constraint doesn't seem to be + enforced by the JVM. */ + } + + if (TREE_THIS_VOLATILE (field_decl)) + field_ref = java_modify_addr_for_volatile (field_ref); + + modify_expr = build2 (MODIFY_EXPR, TREE_TYPE (field_ref), + field_ref, new_value); + + if (TREE_THIS_VOLATILE (field_decl)) + java_add_stmt + (build_call_expr (built_in_decls[BUILT_IN_SYNCHRONIZE], 0)); + + java_add_stmt (modify_expr); + } + else + { + tree temp = build_decl (input_location, + VAR_DECL, NULL_TREE, TREE_TYPE (field_ref)); + java_add_local_var (temp); + + if (TREE_THIS_VOLATILE (field_decl)) + field_ref = java_modify_addr_for_volatile (field_ref); + + modify_expr + = build2 (MODIFY_EXPR, TREE_TYPE (field_ref), temp, field_ref); + java_add_stmt (modify_expr); + + if (TREE_THIS_VOLATILE (field_decl)) + java_add_stmt + (build_call_expr (built_in_decls[BUILT_IN_SYNCHRONIZE], 0)); + + push_value (temp); + } + TREE_THIS_VOLATILE (field_ref) = TREE_THIS_VOLATILE (field_decl); +} + +static void +load_type_state (int pc) +{ + int i; + tree vec = VEC_index (tree, type_states, pc); + int cur_length = TREE_VEC_LENGTH (vec); + stack_pointer = cur_length - DECL_MAX_LOCALS(current_function_decl); + for (i = 0; i < cur_length; i++) + type_map [i] = TREE_VEC_ELT (vec, i); +} + +/* Go over METHOD's bytecode and note instruction starts in + instruction_bits[]. */ + +void +note_instructions (JCF *jcf, tree method) +{ + int PC; + unsigned char* byte_ops; + long length = DECL_CODE_LENGTH (method); + + int saw_index; + jint INT_temp; + +#undef RET /* Defined by config/i386/i386.h */ +#undef PTR +#define BCODE byte_ops +#define BYTE_type_node byte_type_node +#define SHORT_type_node short_type_node +#define INT_type_node int_type_node +#define LONG_type_node long_type_node +#define CHAR_type_node char_type_node +#define PTR_type_node ptr_type_node +#define FLOAT_type_node float_type_node +#define DOUBLE_type_node double_type_node +#define VOID_type_node void_type_node +#define CONST_INDEX_1 (saw_index = 1, IMMEDIATE_u1) +#define CONST_INDEX_2 (saw_index = 1, IMMEDIATE_u2) +#define VAR_INDEX_1 (saw_index = 1, IMMEDIATE_u1) +#define VAR_INDEX_2 (saw_index = 1, IMMEDIATE_u2) + +#define CHECK_PC_IN_RANGE(PC) ((void)1) /* Already handled by verifier. */ + + JCF_SEEK (jcf, DECL_CODE_OFFSET (method)); + byte_ops = jcf->read_ptr; + instruction_bits = XRESIZEVAR (char, instruction_bits, length + 1); + memset (instruction_bits, 0, length + 1); + type_states = VEC_alloc (tree, gc, length + 1); + VEC_safe_grow_cleared (tree, gc, type_states, length + 1); + + /* This pass figures out which PC can be the targets of jumps. */ + for (PC = 0; PC < length;) + { + int oldpc = PC; /* PC at instruction start. */ + instruction_bits [PC] |= BCODE_INSTRUCTION_START; + switch (byte_ops[PC++]) + { +#define JAVAOP(OPNAME, OPCODE, OPKIND, OPERAND_TYPE, OPERAND_VALUE) \ + case OPCODE: \ + PRE_##OPKIND(OPERAND_TYPE, OPERAND_VALUE); \ + break; + +#define NOTE_LABEL(PC) note_label(oldpc, PC) + +#define PRE_PUSHC(OPERAND_TYPE, OPERAND_VALUE) (void)(OPERAND_VALUE); +#define PRE_LOAD(OPERAND_TYPE, OPERAND_VALUE) (void)(OPERAND_VALUE); +#define PRE_STORE(OPERAND_TYPE, OPERAND_VALUE) (void)(OPERAND_VALUE); +#define PRE_STACK(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ +#define PRE_UNOP(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ +#define PRE_BINOP(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ +#define PRE_CONVERT(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ +#define PRE_CONVERT2(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ + +#define PRE_SPECIAL(OPERAND_TYPE, INSTRUCTION) \ + PRE_SPECIAL_##INSTRUCTION(OPERAND_TYPE) +#define PRE_SPECIAL_IINC(OPERAND_TYPE) \ + ((void) IMMEDIATE_u1, (void) IMMEDIATE_s1) +#define PRE_SPECIAL_ENTER(IGNORE) /* nothing */ +#define PRE_SPECIAL_EXIT(IGNORE) /* nothing */ +#define PRE_SPECIAL_THROW(IGNORE) /* nothing */ +#define PRE_SPECIAL_BREAK(IGNORE) /* nothing */ + +/* two forms of wide instructions */ +#define PRE_SPECIAL_WIDE(IGNORE) \ + { \ + int modified_opcode = IMMEDIATE_u1; \ + if (modified_opcode == OPCODE_iinc) \ + { \ + (void) IMMEDIATE_u2; /* indexbyte1 and indexbyte2 */ \ + (void) IMMEDIATE_s2; /* constbyte1 and constbyte2 */ \ + } \ + else \ + { \ + (void) IMMEDIATE_u2; /* indexbyte1 and indexbyte2 */ \ + } \ + } + +#define PRE_IMPL(IGNORE1, IGNORE2) /* nothing */ + +#define PRE_MONITOR(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ + +#define PRE_RETURN(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ +#define PRE_ARRAY(OPERAND_TYPE, SUBOP) \ + PRE_ARRAY_##SUBOP(OPERAND_TYPE) +#define PRE_ARRAY_LOAD(TYPE) /* nothing */ +#define PRE_ARRAY_STORE(TYPE) /* nothing */ +#define PRE_ARRAY_LENGTH(TYPE) /* nothing */ +#define PRE_ARRAY_NEW(TYPE) PRE_ARRAY_NEW_##TYPE +#define PRE_ARRAY_NEW_NUM ((void) IMMEDIATE_u1) +#define PRE_ARRAY_NEW_PTR ((void) IMMEDIATE_u2) +#define PRE_ARRAY_NEW_MULTI ((void) IMMEDIATE_u2, (void) IMMEDIATE_u1) + +#define PRE_TEST(OPERAND_TYPE, OPERAND_VALUE) NOTE_LABEL (oldpc+IMMEDIATE_s2) +#define PRE_COND(OPERAND_TYPE, OPERAND_VALUE) NOTE_LABEL (oldpc+IMMEDIATE_s2) +#define PRE_BRANCH(OPERAND_TYPE, OPERAND_VALUE) \ + saw_index = 0; INT_temp = (OPERAND_VALUE); \ + if (!saw_index) NOTE_LABEL(oldpc + INT_temp); +#define PRE_JSR(OPERAND_TYPE, OPERAND_VALUE) \ + saw_index = 0; INT_temp = (OPERAND_VALUE); \ + NOTE_LABEL (PC); \ + if (!saw_index) NOTE_LABEL(oldpc + INT_temp); + +#define PRE_RET(OPERAND_TYPE, OPERAND_VALUE) (void)(OPERAND_VALUE) + +#define PRE_SWITCH(OPERAND_TYPE, TABLE_OR_LOOKUP) \ + PC = (PC + 3) / 4 * 4; PRE_##TABLE_OR_LOOKUP##_SWITCH + +#define PRE_LOOKUP_SWITCH \ + { jint default_offset = IMMEDIATE_s4; jint npairs = IMMEDIATE_s4; \ + NOTE_LABEL (default_offset+oldpc); \ + if (npairs >= 0) \ + while (--npairs >= 0) { \ + jint match ATTRIBUTE_UNUSED = IMMEDIATE_s4; \ + jint offset = IMMEDIATE_s4; \ + NOTE_LABEL (offset+oldpc); } \ + } + +#define PRE_TABLE_SWITCH \ + { jint default_offset = IMMEDIATE_s4; \ + jint low = IMMEDIATE_s4; jint high = IMMEDIATE_s4; \ + NOTE_LABEL (default_offset+oldpc); \ + if (low <= high) \ + while (low++ <= high) { \ + jint offset = IMMEDIATE_s4; \ + NOTE_LABEL (offset+oldpc); } \ + } + +#define PRE_FIELD(MAYBE_STATIC, PUT_OR_GET) (void)(IMMEDIATE_u2); +#define PRE_OBJECT(MAYBE_STATIC, PUT_OR_GET) (void)(IMMEDIATE_u2); +#define PRE_INVOKE(MAYBE_STATIC, IS_INTERFACE) \ + (void)(IMMEDIATE_u2); \ + PC += 2 * IS_INTERFACE /* for invokeinterface */; + +#include "javaop.def" +#undef JAVAOP + } + } /* for */ +} + +void +expand_byte_code (JCF *jcf, tree method) +{ + int PC; + int i; + const unsigned char *linenumber_pointer; + int dead_code_index = -1; + unsigned char* byte_ops; + long length = DECL_CODE_LENGTH (method); + location_t max_location = input_location; + + stack_pointer = 0; + JCF_SEEK (jcf, DECL_CODE_OFFSET (method)); + byte_ops = jcf->read_ptr; + + /* We make an initial pass of the line number table, to note + which instructions have associated line number entries. */ + linenumber_pointer = linenumber_table; + for (i = 0; i < linenumber_count; i++) + { + int pc = GET_u2 (linenumber_pointer); + linenumber_pointer += 4; + if (pc >= length) + warning (0, "invalid PC in line number table"); + else + { + if ((instruction_bits[pc] & BCODE_HAS_LINENUMBER) != 0) + instruction_bits[pc] |= BCODE_HAS_MULTI_LINENUMBERS; + instruction_bits[pc] |= BCODE_HAS_LINENUMBER; + } + } + + if (! verify_jvm_instructions_new (jcf, byte_ops, length)) + return; + + promote_arguments (); + cache_this_class_ref (method); + cache_cpool_data_ref (); + + /* Translate bytecodes. */ + linenumber_pointer = linenumber_table; + for (PC = 0; PC < length;) + { + if ((instruction_bits [PC] & BCODE_TARGET) != 0 || PC == 0) + { + tree label = lookup_label (PC); + flush_quick_stack (); + if ((instruction_bits [PC] & BCODE_TARGET) != 0) + java_add_stmt (build1 (LABEL_EXPR, void_type_node, label)); + if ((instruction_bits[PC] & BCODE_VERIFIED) != 0) + load_type_state (PC); + } + + if (! (instruction_bits [PC] & BCODE_VERIFIED)) + { + if (dead_code_index == -1) + { + /* This is the start of a region of unreachable bytecodes. + They still need to be processed in order for EH ranges + to get handled correctly. However, we can simply + replace these bytecodes with nops. */ + dead_code_index = PC; + } + + /* Turn this bytecode into a nop. */ + byte_ops[PC] = 0x0; + } + else + { + if (dead_code_index != -1) + { + /* We've just reached the end of a region of dead code. */ + if (extra_warnings) + warning (0, "unreachable bytecode from %d to before %d", + dead_code_index, PC); + dead_code_index = -1; + } + } + + /* Handle possible line number entry for this PC. + + This code handles out-of-order and multiple linenumbers per PC, + but is optimized for the case of line numbers increasing + monotonically with PC. */ + if ((instruction_bits[PC] & BCODE_HAS_LINENUMBER) != 0) + { + if ((instruction_bits[PC] & BCODE_HAS_MULTI_LINENUMBERS) != 0 + || GET_u2 (linenumber_pointer) != PC) + linenumber_pointer = linenumber_table; + while (linenumber_pointer < linenumber_table + linenumber_count * 4) + { + int pc = GET_u2 (linenumber_pointer); + linenumber_pointer += 4; + if (pc == PC) + { + int line = GET_u2 (linenumber_pointer - 2); + input_location = linemap_line_start (line_table, line, 1); + if (input_location > max_location) + max_location = input_location; + if (!(instruction_bits[PC] & BCODE_HAS_MULTI_LINENUMBERS)) + break; + } + } + } + maybe_pushlevels (PC); + PC = process_jvm_instruction (PC, byte_ops, length); + maybe_poplevels (PC); + } /* for */ + + uncache_this_class_ref (method); + + if (dead_code_index != -1) + { + /* We've just reached the end of a region of dead code. */ + if (extra_warnings) + warning (0, "unreachable bytecode from %d to the end of the method", + dead_code_index); + } + + DECL_FUNCTION_LAST_LINE (method) = max_location; +} + +static void +java_push_constant_from_pool (JCF *jcf, int index) +{ + tree c; + if (JPOOL_TAG (jcf, index) == CONSTANT_String) + { + tree name; + name = get_name_constant (jcf, JPOOL_USHORT1 (jcf, index)); + index = alloc_name_constant (CONSTANT_String, name); + c = build_ref_from_constant_pool (index); + c = convert (promote_type (string_type_node), c); + } + else if (JPOOL_TAG (jcf, index) == CONSTANT_Class + || JPOOL_TAG (jcf, index) == CONSTANT_ResolvedClass) + { + tree record = get_class_constant (jcf, index); + c = build_class_ref (record); + } + else + c = get_constant (jcf, index); + push_value (c); +} + +int +process_jvm_instruction (int PC, const unsigned char* byte_ops, + long length ATTRIBUTE_UNUSED) +{ + const char *opname; /* Temporary ??? */ + int oldpc = PC; /* PC at instruction start. */ + + /* If the instruction is at the beginning of an exception handler, + replace the top of the stack with the thrown object reference. */ + if (instruction_bits [PC] & BCODE_EXCEPTION_TARGET) + { + /* Note that the verifier will not emit a type map at all for + dead exception handlers. In this case we just ignore the + situation. */ + if ((instruction_bits[PC] & BCODE_VERIFIED) != 0) + { + tree type = pop_type (promote_type (throwable_type_node)); + push_value (build_exception_object_ref (type)); + } + } + + switch (byte_ops[PC++]) + { +#define JAVAOP(OPNAME, OPCODE, OPKIND, OPERAND_TYPE, OPERAND_VALUE) \ + case OPCODE: \ + opname = #OPNAME; \ + OPKIND(OPERAND_TYPE, OPERAND_VALUE); \ + break; + +#define RET(OPERAND_TYPE, OPERAND_VALUE) \ + { \ + int saw_index = 0; \ + int index = OPERAND_VALUE; \ + (void) saw_index; /* Avoid set but not used warning. */ \ + build_java_ret \ + (find_local_variable (index, return_address_type_node, oldpc)); \ + } + +#define JSR(OPERAND_TYPE, OPERAND_VALUE) \ + { \ + /* OPERAND_VALUE may have side-effects on PC */ \ + int opvalue = OPERAND_VALUE; \ + build_java_jsr (oldpc + opvalue, PC); \ + } + +/* Push a constant onto the stack. */ +#define PUSHC(OPERAND_TYPE, OPERAND_VALUE) \ + { int saw_index = 0; int ival = (OPERAND_VALUE); \ + if (saw_index) java_push_constant_from_pool (current_jcf, ival); \ + else expand_java_pushc (ival, OPERAND_TYPE##_type_node); } + +/* internal macro added for use by the WIDE case */ +#define LOAD_INTERNAL(OPTYPE, OPVALUE) \ + expand_load_internal (OPVALUE, type_map[OPVALUE], oldpc); + +/* Push local variable onto the opcode stack. */ +#define LOAD(OPERAND_TYPE, OPERAND_VALUE) \ + { \ + /* have to do this since OPERAND_VALUE may have side-effects */ \ + int opvalue = OPERAND_VALUE; \ + LOAD_INTERNAL(OPERAND_TYPE##_type_node, opvalue); \ + } + +#define RETURN(OPERAND_TYPE, OPERAND_VALUE) \ + expand_java_return (OPERAND_TYPE##_type_node) + +#define REM_EXPR TRUNC_MOD_EXPR +#define BINOP(OPERAND_TYPE, OPERAND_VALUE) \ + expand_java_binop (OPERAND_TYPE##_type_node, OPERAND_VALUE##_EXPR) + +#define FIELD(IS_STATIC, IS_PUT) \ + expand_java_field_op (IS_STATIC, IS_PUT, IMMEDIATE_u2) + +#define TEST(OPERAND_TYPE, CONDITION) \ + expand_test (CONDITION##_EXPR, OPERAND_TYPE##_type_node, oldpc+IMMEDIATE_s2) + +#define COND(OPERAND_TYPE, CONDITION) \ + expand_cond (CONDITION##_EXPR, OPERAND_TYPE##_type_node, oldpc+IMMEDIATE_s2) + +#define BRANCH(OPERAND_TYPE, OPERAND_VALUE) \ + BRANCH_##OPERAND_TYPE (OPERAND_VALUE) + +#define BRANCH_GOTO(OPERAND_VALUE) \ + expand_java_goto (oldpc + OPERAND_VALUE) + +#define BRANCH_CALL(OPERAND_VALUE) \ + expand_java_call (oldpc + OPERAND_VALUE, oldpc) + +#if 0 +#define BRANCH_RETURN(OPERAND_VALUE) \ + { \ + tree type = OPERAND_TYPE##_type_node; \ + tree value = find_local_variable (OPERAND_VALUE, type, oldpc); \ + expand_java_ret (value); \ + } +#endif + +#define NOT_IMPL(OPERAND_TYPE, OPERAND_VALUE) \ + fprintf (stderr, "%3d: %s ", oldpc, opname); \ + fprintf (stderr, "(not implemented)\n") +#define NOT_IMPL1(OPERAND_VALUE) \ + fprintf (stderr, "%3d: %s ", oldpc, opname); \ + fprintf (stderr, "(not implemented)\n") + +#define BRANCH_RETURN(OPERAND_VALUE) NOT_IMPL1(OPERAND_VALUE) + +#define STACK(SUBOP, COUNT) STACK_##SUBOP (COUNT) + +#define STACK_POP(COUNT) java_stack_pop (COUNT) + +#define STACK_SWAP(COUNT) java_stack_swap() + +#define STACK_DUP(COUNT) java_stack_dup (COUNT, 0) +#define STACK_DUPx1(COUNT) java_stack_dup (COUNT, 1) +#define STACK_DUPx2(COUNT) java_stack_dup (COUNT, 2) + +#define SWITCH(OPERAND_TYPE, TABLE_OR_LOOKUP) \ + PC = (PC + 3) / 4 * 4; TABLE_OR_LOOKUP##_SWITCH + +#define LOOKUP_SWITCH \ + { jint default_offset = IMMEDIATE_s4; jint npairs = IMMEDIATE_s4; \ + tree selector = pop_value (INT_type_node); \ + tree switch_expr = expand_java_switch (selector, oldpc + default_offset); \ + while (--npairs >= 0) \ + { \ + jint match = IMMEDIATE_s4; jint offset = IMMEDIATE_s4; \ + expand_java_add_case (switch_expr, match, oldpc + offset); \ + } \ + } + +#define TABLE_SWITCH \ + { jint default_offset = IMMEDIATE_s4; \ + jint low = IMMEDIATE_s4; jint high = IMMEDIATE_s4; \ + tree selector = pop_value (INT_type_node); \ + tree switch_expr = expand_java_switch (selector, oldpc + default_offset); \ + for (; low <= high; low++) \ + { \ + jint offset = IMMEDIATE_s4; \ + expand_java_add_case (switch_expr, low, oldpc + offset); \ + } \ + } + +#define INVOKE(MAYBE_STATIC, IS_INTERFACE) \ + { int opcode = byte_ops[PC-1]; \ + int method_ref_index = IMMEDIATE_u2; \ + int nargs; \ + if (IS_INTERFACE) { nargs = IMMEDIATE_u1; (void) IMMEDIATE_u1; } \ + else nargs = -1; \ + expand_invoke (opcode, method_ref_index, nargs); \ + } + +/* Handle new, checkcast, instanceof */ +#define OBJECT(TYPE, OP) \ + expand_java_##OP (get_class_constant (current_jcf, IMMEDIATE_u2)) + +#define ARRAY(OPERAND_TYPE, SUBOP) ARRAY_##SUBOP(OPERAND_TYPE) + +#define ARRAY_LOAD(OPERAND_TYPE) \ + { \ + expand_java_arrayload( OPERAND_TYPE##_type_node ); \ + } + +#define ARRAY_STORE(OPERAND_TYPE) \ + { \ + expand_java_arraystore( OPERAND_TYPE##_type_node ); \ + } + +#define ARRAY_LENGTH(OPERAND_TYPE) expand_java_array_length(); +#define ARRAY_NEW(OPERAND_TYPE) ARRAY_NEW_##OPERAND_TYPE() +#define ARRAY_NEW_PTR() \ + push_value (build_anewarray (get_class_constant (current_jcf, \ + IMMEDIATE_u2), \ + pop_value (int_type_node))); +#define ARRAY_NEW_NUM() \ + { \ + int atype = IMMEDIATE_u1; \ + push_value (build_newarray (atype, pop_value (int_type_node)));\ + } +#define ARRAY_NEW_MULTI() \ + { \ + tree klass = get_class_constant (current_jcf, IMMEDIATE_u2 ); \ + int ndims = IMMEDIATE_u1; \ + expand_java_multianewarray( klass, ndims ); \ + } + +#define UNOP(OPERAND_TYPE, OPERAND_VALUE) \ + push_value (fold_build1 (NEGATE_EXPR, OPERAND_TYPE##_type_node, \ + pop_value (OPERAND_TYPE##_type_node))); + +#define CONVERT2(FROM_TYPE, TO_TYPE) \ + { \ + push_value (build1 (NOP_EXPR, int_type_node, \ + (convert (TO_TYPE##_type_node, \ + pop_value (FROM_TYPE##_type_node))))); \ + } + +#define CONVERT(FROM_TYPE, TO_TYPE) \ + { \ + push_value (convert (TO_TYPE##_type_node, \ + pop_value (FROM_TYPE##_type_node))); \ + } + +/* internal macro added for use by the WIDE case + Added TREE_TYPE (decl) assignment, apbianco */ +#define STORE_INTERNAL(OPTYPE, OPVALUE) \ + { \ + tree decl, value; \ + int index = OPVALUE; \ + tree type = OPTYPE; \ + value = pop_value (type); \ + type = TREE_TYPE (value); \ + decl = find_local_variable (index, type, oldpc); \ + set_local_type (index, type); \ + java_add_stmt (build2 (MODIFY_EXPR, type, decl, value)); \ + } + +#define STORE(OPERAND_TYPE, OPERAND_VALUE) \ + { \ + /* have to do this since OPERAND_VALUE may have side-effects */ \ + int opvalue = OPERAND_VALUE; \ + STORE_INTERNAL(OPERAND_TYPE##_type_node, opvalue); \ + } + +#define SPECIAL(OPERAND_TYPE, INSTRUCTION) \ + SPECIAL_##INSTRUCTION(OPERAND_TYPE) + +#define SPECIAL_ENTER(IGNORED) MONITOR_OPERATION (soft_monitorenter_node) +#define SPECIAL_EXIT(IGNORED) MONITOR_OPERATION (soft_monitorexit_node) + +#define MONITOR_OPERATION(call) \ + { \ + tree o = pop_value (ptr_type_node); \ + tree c; \ + flush_quick_stack (); \ + c = build_java_monitor (call, o); \ + TREE_SIDE_EFFECTS (c) = 1; \ + java_add_stmt (c); \ + } + +#define SPECIAL_IINC(IGNORED) \ + { \ + unsigned int local_var_index = IMMEDIATE_u1; \ + int ival = IMMEDIATE_s1; \ + expand_iinc(local_var_index, ival, oldpc); \ + } + +#define SPECIAL_WIDE(IGNORED) \ + { \ + int modified_opcode = IMMEDIATE_u1; \ + unsigned int local_var_index = IMMEDIATE_u2; \ + switch (modified_opcode) \ + { \ + case OPCODE_iinc: \ + { \ + int ival = IMMEDIATE_s2; \ + expand_iinc (local_var_index, ival, oldpc); \ + break; \ + } \ + case OPCODE_iload: \ + case OPCODE_lload: \ + case OPCODE_fload: \ + case OPCODE_dload: \ + case OPCODE_aload: \ + { \ + /* duplicate code from LOAD macro */ \ + LOAD_INTERNAL(operand_type[modified_opcode], local_var_index); \ + break; \ + } \ + case OPCODE_istore: \ + case OPCODE_lstore: \ + case OPCODE_fstore: \ + case OPCODE_dstore: \ + case OPCODE_astore: \ + { \ + STORE_INTERNAL(operand_type[modified_opcode], local_var_index); \ + break; \ + } \ + default: \ + error ("unrecogized wide sub-instruction"); \ + } \ + } + +#define SPECIAL_THROW(IGNORED) \ + build_java_athrow (pop_value (throwable_type_node)) + +#define SPECIAL_BREAK NOT_IMPL1 +#define IMPL NOT_IMPL + +#include "javaop.def" +#undef JAVAOP + default: + fprintf (stderr, "%3d: unknown(%3d)\n", oldpc, byte_ops[PC]); + } + return PC; +} + +/* Return the opcode at PC in the code section pointed to by + CODE_OFFSET. */ + +static unsigned char +peek_opcode_at_pc (JCF *jcf, int code_offset, int pc) +{ + unsigned char opcode; + long absolute_offset = (long)JCF_TELL (jcf); + + JCF_SEEK (jcf, code_offset); + opcode = jcf->read_ptr [pc]; + JCF_SEEK (jcf, absolute_offset); + return opcode; +} + +/* Some bytecode compilers are emitting accurate LocalVariableTable + attributes. Here's an example: + + PC <t>store_<n> + PC+1 ... + + Attribute "LocalVariableTable" + slot #<n>: ... (PC: PC+1 length: L) + + This is accurate because the local in slot <n> really exists after + the opcode at PC is executed, hence from PC+1 to PC+1+L. + + This procedure recognizes this situation and extends the live range + of the local in SLOT to START_PC-1 or START_PC-2 (depending on the + length of the store instruction.) + + This function is used by `give_name_to_locals' so that a local's + DECL features a DECL_LOCAL_START_PC such that the first related + store operation will use DECL as a destination, not an unrelated + temporary created for the occasion. + + This function uses a global (instruction_bits) `note_instructions' should + have allocated and filled properly. */ + +int +maybe_adjust_start_pc (struct JCF *jcf, int code_offset, + int start_pc, int slot) +{ + int first, index, opcode; + int pc, insn_pc; + int wide_found = 0; + + if (!start_pc) + return start_pc; + + first = index = -1; + + /* Find last previous instruction and remember it */ + for (pc = start_pc-1; pc; pc--) + if (instruction_bits [pc] & BCODE_INSTRUCTION_START) + break; + insn_pc = pc; + + /* Retrieve the instruction, handle `wide'. */ + opcode = (int) peek_opcode_at_pc (jcf, code_offset, pc++); + if (opcode == OPCODE_wide) + { + wide_found = 1; + opcode = (int) peek_opcode_at_pc (jcf, code_offset, pc++); + } + + switch (opcode) + { + case OPCODE_astore_0: + case OPCODE_astore_1: + case OPCODE_astore_2: + case OPCODE_astore_3: + first = OPCODE_astore_0; + break; + + case OPCODE_istore_0: + case OPCODE_istore_1: + case OPCODE_istore_2: + case OPCODE_istore_3: + first = OPCODE_istore_0; + break; + + case OPCODE_lstore_0: + case OPCODE_lstore_1: + case OPCODE_lstore_2: + case OPCODE_lstore_3: + first = OPCODE_lstore_0; + break; + + case OPCODE_fstore_0: + case OPCODE_fstore_1: + case OPCODE_fstore_2: + case OPCODE_fstore_3: + first = OPCODE_fstore_0; + break; + + case OPCODE_dstore_0: + case OPCODE_dstore_1: + case OPCODE_dstore_2: + case OPCODE_dstore_3: + first = OPCODE_dstore_0; + break; + + case OPCODE_astore: + case OPCODE_istore: + case OPCODE_lstore: + case OPCODE_fstore: + case OPCODE_dstore: + index = peek_opcode_at_pc (jcf, code_offset, pc); + if (wide_found) + { + int other = peek_opcode_at_pc (jcf, code_offset, ++pc); + index = (other << 8) + index; + } + break; + } + + /* Now we decide: first >0 means we have a <t>store_<n>, index >0 + means we have a <t>store. */ + if ((first > 0 && opcode - first == slot) || (index > 0 && index == slot)) + start_pc = insn_pc; + + return start_pc; +} + +/* Force the (direct) sub-operands of NODE to be evaluated in left-to-right + order, as specified by Java Language Specification. + + The problem is that while expand_expr will evaluate its sub-operands in + left-to-right order, for variables it will just return an rtx (i.e. + an lvalue) for the variable (rather than an rvalue). So it is possible + that a later sub-operand will change the register, and when the + actual operation is done, it will use the new value, when it should + have used the original value. + + We fix this by using save_expr. This forces the sub-operand to be + copied into a fresh virtual register, + + For method invocation, we modify the arguments so that a + left-to-right order evaluation is performed. Saved expressions + will, in CALL_EXPR order, be reused when the call will be expanded. + + We also promote outgoing args if needed. */ + +tree +force_evaluation_order (tree node) +{ + if (flag_syntax_only) + return node; + if (TREE_CODE (node) == CALL_EXPR + || (TREE_CODE (node) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR + && TREE_CODE (TREE_OPERAND (node, 1)) == SAVE_EXPR)) + { + tree call, cmp; + int i, nargs; + + /* Account for wrapped around ctors. */ + if (TREE_CODE (node) == COMPOUND_EXPR) + call = TREE_OPERAND (node, 0); + else + call = node; + + nargs = call_expr_nargs (call); + + /* This reverses the evaluation order. This is a desired effect. */ + for (i = 0, cmp = NULL_TREE; i < nargs; i++) + { + tree arg = CALL_EXPR_ARG (call, i); + /* Promote types smaller than integer. This is required by + some ABIs. */ + tree type = TREE_TYPE (arg); + tree saved; + if (targetm.calls.promote_prototypes (type) + && INTEGRAL_TYPE_P (type) + && INT_CST_LT_UNSIGNED (TYPE_SIZE (type), + TYPE_SIZE (integer_type_node))) + arg = fold_convert (integer_type_node, arg); + + saved = save_expr (force_evaluation_order (arg)); + cmp = (cmp == NULL_TREE ? saved : + build2 (COMPOUND_EXPR, void_type_node, cmp, saved)); + + CALL_EXPR_ARG (call, i) = saved; + } + + if (cmp && TREE_CODE (cmp) == COMPOUND_EXPR) + TREE_SIDE_EFFECTS (cmp) = 1; + + if (cmp) + { + cmp = build2 (COMPOUND_EXPR, TREE_TYPE (node), cmp, node); + if (TREE_TYPE (cmp) != void_type_node) + cmp = save_expr (cmp); + TREE_SIDE_EFFECTS (cmp) = 1; + node = cmp; + } + } + return node; +} + +/* Build a node to represent empty statements and blocks. */ + +tree +build_java_empty_stmt (void) +{ + tree t = build_empty_stmt (input_location); + return t; +} + +/* Promote all args of integral type before generating any code. */ + +static void +promote_arguments (void) +{ + int i; + tree arg; + for (arg = DECL_ARGUMENTS (current_function_decl), i = 0; + arg != NULL_TREE; arg = DECL_CHAIN (arg), i++) + { + tree arg_type = TREE_TYPE (arg); + if (INTEGRAL_TYPE_P (arg_type) + && TYPE_PRECISION (arg_type) < 32) + { + tree copy = find_local_variable (i, integer_type_node, -1); + java_add_stmt (build2 (MODIFY_EXPR, integer_type_node, + copy, + fold_convert (integer_type_node, arg))); + } + if (TYPE_IS_WIDE (arg_type)) + i++; + } +} + +/* Create a local variable that points to the constant pool. */ + +static void +cache_cpool_data_ref (void) +{ + if (optimize) + { + tree cpool; + tree d = build_constant_data_ref (flag_indirect_classes); + tree cpool_ptr = build_decl (input_location, VAR_DECL, NULL_TREE, + build_pointer_type (TREE_TYPE (d))); + java_add_local_var (cpool_ptr); + TREE_CONSTANT (cpool_ptr) = 1; + + java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (cpool_ptr), + cpool_ptr, build_address_of (d))); + cpool = build1 (INDIRECT_REF, TREE_TYPE (d), cpool_ptr); + TREE_THIS_NOTRAP (cpool) = 1; + TYPE_CPOOL_DATA_REF (output_class) = cpool; + } +} + +#include "gt-java-expr.h" |