/* Process expressions for the GNU compiler for the Java(TM) language.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008 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 2, or (at your option)
+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,
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 COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.
+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.
#include "parse.h"
#include "toplev.h"
#include "except.h"
+#include "tm_p.h"
#include "ggc.h"
-#include "tree-gimple.h"
+#include "tree-iterator.h"
+#include "gimple.h"
#include "target.h"
static void flush_quick_stack (void);
static void expand_java_goto (int);
static tree expand_java_switch (tree, int);
static void expand_java_add_case (tree, int, int);
-#if 0
-static void expand_java_call (int, int);
-static void expand_java_ret (tree);
-#endif
static tree pop_arguments (tree);
static void expand_invoke (int, int, int);
static void expand_java_field_op (int, int, int);
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];
/* A free-list of unused permanent TREE_LIST nodes. */
static GTY((deletable)) tree tree_list_free_list;
+/* 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. */
TREE_CHAIN (node) = quick_stack;
quick_stack = node;
}
+ /* 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.
static int
type_assertion_eq (const void * k1_p, const void * k2_p)
{
- type_assertion k1 = *(type_assertion *)k1_p;
- type_assertion k2 = *(type_assertion *)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);
static hashval_t
type_assertion_hash (const void *p)
{
- const type_assertion *k_p = 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);
- hash = iterative_hash (&k_p->op1, sizeof k_p->op1, hash);
- return iterative_hash (&k_p->op2, sizeof k_p->op2, hash);
+
+ 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.
- CLASS is the class for which this assertion will be evaluated by the
+ 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 class, int assertion_code, tree op1, tree op2)
+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 (class);
+ assertions_htab = TYPE_ASSERTIONS (klass);
if (assertions_htab == NULL)
{
assertions_htab = htab_create_ggc (7, type_assertion_hash,
tree decl1, decl2;
if (stack_pointer < 2
- || (type1 = stack_type_map[stack_pointer - 1]) == TYPE_UNKNOWN
- || (type2 = stack_type_map[stack_pointer - 2]) == TYPE_UNKNOWN
- || type1 == TYPE_SECOND || type2 == TYPE_SECOND
+ || (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 ();
flush_quick_stack ();
decl1 = find_stack_slot (stack_pointer - 1, type1);
decl2 = find_stack_slot (stack_pointer - 2, type2);
- temp = build_decl (VAR_DECL, NULL_TREE, type1);
+ 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,
{
tree call;
- call = build3 (CALL_EXPR,
- void_type_node,
- build_address_of (throw_node),
- build_tree_list (NULL_TREE, node),
- NULL_TREE);
+ 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);
static tree
build_java_throw_out_of_bounds_exception (tree index)
{
- tree node = build3 (CALL_EXPR, int_type_node,
- build_address_of (soft_badarrayindex_node),
- build_tree_list (NULL_TREE, index), NULL_TREE);
+ 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);
}
expr = build3 (COND_EXPR, TREE_TYPE (expr),
build2 (EQ_EXPR, boolean_type_node,
expr, null_pointer_node),
- build3 (CALL_EXPR, void_type_node,
- build_address_of (soft_nullpointer_node),
- NULL_TREE, NULL_TREE),
+ build_call_nary (void_type_node,
+ build_address_of (soft_nullpointer_node),
+ 0),
expr);
}
tree
build_java_arrayaccess (tree array, tree type, tree index)
{
- tree node, throw = NULL_TREE;
+ 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)))
{
len);
if (! integer_zerop (test))
{
- throw = build2 (TRUTH_ANDIF_EXPR, int_type_node, test,
- build_java_throw_out_of_bounds_exception (index));
+ 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 ) = 1;
+ 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 != NULL_TREE)
- index = build2 (COMPOUND_EXPR, int_type_node, throw, index);
-
+ 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);
-
- node = build4 (ARRAY_REF, type, ref, index, NULL_TREE, NULL_TREE);
- return node;
+
+ /* 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
}
/* Build an invocation of _Jv_CheckArrayStore */
- check = build3 (CALL_EXPR, void_type_node,
- build_address_of (soft_checkarraystore_node),
- tree_cons (NULL_TREE, array,
- build_tree_list (NULL_TREE, object)),
- NULL_TREE);
+ check = build_call_nary (void_type_node,
+ build_address_of (soft_checkarraystore_node),
+ 2, array, object);
TREE_SIDE_EFFECTS (check) = 1;
return check;
host_integerp (length, 0) == INTEGER_CST
? tree_low_cst (length, 0) : -1);
- /* If compiling to native, pass a reference to the primitive type class
- and save the runtime some work. However, the bytecode generator
- expects to find the type_code int here. */
- if (flag_emit_class_files)
- type_arg = build_int_cst (NULL_TREE, atype_value);
- else
- type_arg = build_class_ref (prim_type);
+ /* Pass a reference to the primitive type class and save the runtime
+ some work. */
+ type_arg = build_class_ref (prim_type);
- return build3 (CALL_EXPR, promote_type (type),
- build_address_of (soft_newarray_node),
- tree_cons (NULL_TREE,
- type_arg,
- build_tree_list (NULL_TREE, length)),
- NULL_TREE);
+ 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
host_integerp (length, 0)
? tree_low_cst (length, 0) : -1);
- return build3 (CALL_EXPR, promote_type (type),
- build_address_of (soft_anewarray_node),
- tree_cons (NULL_TREE, length,
- tree_cons (NULL_TREE, build_class_ref (class_type),
- build_tree_list (NULL_TREE,
- null_pointer_node))),
- NULL_TREE);
+ 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]'. */
for( i = 0; i < ndim; i++ )
args = tree_cons (NULL_TREE, pop_value (int_type_node), args);
- push_value (build3 (CALL_EXPR,
- promote_type (class_type),
- build_address_of (soft_multianewarray_node),
- tree_cons (NULL_TREE, build_class_ref (class_type),
- tree_cons (NULL_TREE,
- build_int_cst (NULL_TREE, ndim),
- args)),
- NULL_TREE));
+ args = tree_cons (NULL_TREE,
+ build_class_ref (class_type),
+ tree_cons (NULL_TREE,
+ build_int_cst (NULL_TREE, ndim),
+ args));
+
+ push_value (build_call_list (promote_type (class_type),
+ build_address_of (soft_multianewarray_node),
+ args));
}
/* ARRAY[INDEX] <- RHS. build_java_check_indexed_type makes sure that
&& TYPE_PRECISION (rhs_type_node) <= 32) ?
int_type_node : rhs_type_node);
tree index = pop_value (int_type_node);
- tree array_type, array;
+ tree array_type, array, temp, access;
/* If we're processing an `aaload' we might as well just pick
`Object'. */
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);
}
- array = build_java_arrayaccess (array, rhs_type_node, index);
- java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (array), array, rhs_node));
+ 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
static tree
build_java_monitor (tree call, tree object)
{
- return build3 (CALL_EXPR,
- void_type_node,
- build_address_of (call),
- build_tree_list (NULL_TREE, object),
- NULL_TREE);
+ return build_call_nary (void_type_node,
+ build_address_of (call),
+ 1, object);
}
/* Emit code for one of the PUSHC instructions. */
retval = build1(NOP_EXPR, TREE_TYPE(res), retval);
TREE_SIDE_EFFECTS (retval) = 1;
- java_add_stmt (build1 (RETURN_EXPR, TREE_TYPE (retval), retval));
+ java_add_stmt (build1 (RETURN_EXPR, void_type_node, retval));
}
}
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 (VAR_DECL, NULL_TREE, type);
+ 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);
? alloc_object_node
: alloc_no_finalizer_node);
- return build3 (CALL_EXPR, promote_type (type),
- build_address_of (alloc_node),
- build_tree_list (NULL_TREE, build_class_ref (type)),
- NULL_TREE);
+ return build_call_nary (promote_type (type),
+ build_address_of (alloc_node),
+ 1, build_class_ref (type));
}
static void
if (! CLASS_LOADED_P (type))
load_class (type, 1);
safe_layout_class (type);
- push_value (build3 (CALL_EXPR, promote_type (type),
- build_address_of (alloc_node),
- build_tree_list (NULL_TREE, build_class_ref (type)),
- NULL_TREE));
+ 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
}
else
{
- expr = build3 (CALL_EXPR, itype,
- build_address_of (soft_instanceof_node),
- tree_cons (NULL_TREE, value,
- build_tree_list (NULL_TREE,
- build_class_ref (type))),
- NULL_TREE);
+ 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;
expand_java_CHECKCAST (tree type)
{
tree value = pop_value (ptr_type_node);
- value = build3 (CALL_EXPR, promote_type (type),
- build_address_of (soft_checkcast_node),
- tree_cons (NULL_TREE, build_class_ref (type),
- build_tree_list (NULL_TREE, value)),
- NULL_TREE);
+ value = build_call_nary (promote_type (type),
+ build_address_of (soft_checkcast_node),
+ 2, build_class_ref (type), value);
push_value (value);
}
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));
- update_aliases (local_var, local_var_index, pc);
}
}
gcc_assert (call);
- call = build3 (CALL_EXPR, type,
- build_address_of (call),
- tree_cons (NULL_TREE, arg1,
- build_tree_list (NULL_TREE, arg2)),
- NULL_TREE);
-
+ call = build_call_nary (type, build_address_of (call), 2, arg1, arg2);
return call;
}
{
case URSHIFT_EXPR:
{
- tree u_type = java_unsigned_type (type);
+ 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);
arg1 = convert (double_type_node, arg1);
arg2 = convert (double_type_node, arg2);
}
- call = build3 (CALL_EXPR, double_type_node,
- build_address_of (soft_fmod_node),
- tree_cons (NULL_TREE, arg1,
- build_tree_list (NULL_TREE, arg2)),
- NULL_TREE);
+ 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;
}
else
{
- int check = (flag_check_references
- && ! (DECL_P (self_value)
- && DECL_NAME (self_value) == this_identifier_node));
-
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, &TYPE_OTABLE_METHODS (output_class)));
+ (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,
= build3 (COND_EXPR, TREE_TYPE (field_offset),
build2 (EQ_EXPR, boolean_type_node,
field_offset, integer_zero_node),
- build3 (CALL_EXPR, void_type_node,
- build_address_of (soft_nosuchfield_node),
- build_tree_list (NULL_TREE, otable_index),
- NULL_TREE),
+ 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 (PLUS_EXPR,
- build_pointer_type (TREE_TYPE (field_decl)),
+ = 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);
}
{
/* The type of the address of a label is return_address_type_node. */
tree decl = create_label_decl (name);
- LABEL_PC (decl) = pc;
return pushdecl (decl);
}
}
create_label_decl (tree name)
{
tree decl;
- decl = build_decl (LABEL_DECL, name,
+ 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. */
+/* 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)
{
java_add_stmt (switch_expr);
x = build3 (CASE_LABEL_EXPR, void_type_node, NULL_TREE, NULL_TREE,
- create_artificial_label ());
+ 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));
value = build_int_cst (TREE_TYPE (switch_expr), match);
x = build3 (CASE_LABEL_EXPR, void_type_node, value, NULL_TREE,
- create_artificial_label ());
+ 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));
if (always_initialize_class_p)
{
- init = build3 (CALL_EXPR, void_type_node,
- build_address_of (soft_initclass_node),
- build_tree_list (NULL_TREE, build_class_ref (clas)),
- NULL_TREE);
+ init = build_call_nary (void_type_node,
+ build_address_of (soft_initclass_node),
+ 1, build_class_ref (clas));
TREE_SIDE_EFFECTS (init) = 1;
}
else
{
/* Build a declaration and mark it as a flag used to track
static class initializations. */
- decl = build_decl (VAR_DECL, NULL_TREE,
+ decl = build_decl (input_location, VAR_DECL, NULL_TREE,
boolean_type_node);
MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl);
- LOCAL_CLASS_INITIALIZATION_FLAG (decl) = 1;
DECL_CONTEXT (decl) = current_function_decl;
- DECL_FUNCTION_INIT_TEST_CLASS (decl) = clas;
- /* Tell the check-init code to ignore this decl when not
- optimizing class initialization. */
- if (!STATIC_CLASS_INIT_OPT_P ())
- DECL_BIT_INDEX (decl) = -1;
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 = build3 (CALL_EXPR, void_type_node,
- build_address_of (soft_initclass_node),
- build_tree_list (NULL_TREE, build_class_ref (clas)),
- NULL_TREE);
+ 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,
return init;
}
+\f
+
+/* 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;
+ tree (*rewrite_arglist) (tree arglist);
+} rewrite_rule;
+
+/* Add __builtin_return_address(0) to the end of an arglist. */
+
+
+static tree
+rewrite_arglist_getcaller (tree arglist)
+{
+ tree retaddr
+ = build_call_expr (built_in_decls[BUILT_IN_RETURN_ADDRESS],
+ 1, integer_zero_node);
+
+ DECL_UNINLINABLE (current_function_decl) = 1;
+
+ return chainon (arglist,
+ tree_cons (NULL_TREE, retaddr,
+ NULL_TREE));
+}
+
+/* Add this.class to the end of an arglist. */
+
+static tree
+rewrite_arglist_getclass (tree arglist)
+{
+ return chainon (arglist,
+ tree_cons (NULL_TREE, build_class_ref (output_class),
+ NULL_TREE));
+}
+
+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, tree *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)
+ *arg_list_p = p->rewrite_arglist (*arg_list_p);
+ *method_signature_p = get_identifier (p->new_signature);
+ *special = integer_one_node;
+
+ break;
+ }
+ }
+ }
+}
+
+\f
+
tree
build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED,
tree self_type, tree method_signature ATTRIBUTE_UNUSED,
- tree arg_list ATTRIBUTE_UNUSED)
+ tree arg_list ATTRIBUTE_UNUSED, tree special)
{
tree func;
if (is_compiled_class (self_type))
else
{
tree table_index
- = build_int_cst (NULL_TREE, get_symbol_table_index
- (method, &TYPE_ATABLE_METHODS (output_class)));
+ = 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))),
method_index++;
}
method_index *= int_size_in_bytes (method_type_node);
- ref = fold_build2 (PLUS_EXPR, method_ptr_type_node,
- ref, build_int_cst (NULL_TREE, method_index));
+ 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),
reused. */
int
-get_symbol_table_index (tree t, tree *symbol_table)
+get_symbol_table_index (tree t, tree special, tree *symbol_table)
{
int i = 1;
tree method_list;
if (*symbol_table == NULL_TREE)
{
- *symbol_table = build_tree_list (t, t);
+ *symbol_table = build_tree_list (special, t);
return 1;
}
while (1)
{
tree value = TREE_VALUE (method_list);
- if (value == t)
+ tree purpose = TREE_PURPOSE (method_list);
+ if (value == t && purpose == special)
return i;
i++;
if (TREE_CHAIN (method_list) == NULL_TREE)
method_list = TREE_CHAIN (method_list);
}
- TREE_CHAIN (method_list) = build_tree_list (t, t);
+ TREE_CHAIN (method_list) = build_tree_list (special, t);
return i;
}
tree
-build_invokevirtual (tree dtable, tree method)
+build_invokevirtual (tree dtable, tree method, tree special)
{
tree func;
tree nativecode_ptr_ptr_type_node
otable_index
= build_int_cst (NULL_TREE, get_symbol_table_index
- (method, &TYPE_OTABLE_METHODS (output_class)));
+ (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);
size_int (TARGET_VTABLE_USES_DESCRIPTORS));
}
- func = fold_build2 (PLUS_EXPR, nativecode_ptr_ptr_type_node, dtable,
- convert (nativecode_ptr_ptr_type_node, method_index));
+ 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 = build1 (INDIRECT_REF, nativecode_ptr_type_node, func);
+ {
+ func = fold_convert (nativecode_ptr_ptr_type_node, func);
+ func = build1 (INDIRECT_REF, nativecode_ptr_type_node, func);
+ }
return func;
}
tree
build_invokeinterface (tree dtable, tree method)
{
- tree lookup_arg;
tree interface;
tree idx;
{
int itable_index
= 2 * (get_symbol_table_index
- (method, &TYPE_ITABLE_METHODS (output_class)));
+ (method, NULL_TREE, &TYPE_ITABLE_METHODS (output_class)));
interface
= build4 (ARRAY_REF,
TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))),
interface = build_class_ref (interface);
}
- lookup_arg = tree_cons (NULL_TREE, dtable,
- tree_cons (NULL_TREE, interface,
- build_tree_list (NULL_TREE, idx)));
-
- return build3 (CALL_EXPR, ptr_type_node,
- build_address_of (soft_lookupinterfacemethod_node),
- lookup_arg, NULL_TREE);
+ return build_call_nary (ptr_type_node,
+ build_address_of (soft_lookupinterfacemethod_node),
+ 3, dtable, interface, idx);
}
/* Expand one of the invoke_* opcodes.
tree call, func, method, arg_list, method_type;
tree check = NULL_TREE;
+ tree special = NULL_TREE;
+
if (! CLASS_LOADED_P (self_type))
{
load_class (self_type, 1);
arg_list = pop_arguments (TYPE_ARG_TYPES (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);
+ method_signature, arg_list, special);
else if (opcode == OPCODE_invokespecial
|| (opcode == OPCODE_invokevirtual
&& (METHOD_PRIVATE (method)
TREE_VALUE (arg_list) = 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);
+ 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);
+ func = build_invokevirtual (dtable, method, special);
else
func = build_invokeinterface (dtable, method);
}
else
func = build1 (NOP_EXPR, build_pointer_type (method_type), func);
- call = build3 (CALL_EXPR, TREE_TYPE (method_type),
- func, arg_list, NULL_TREE);
+ call = build_call_list (TREE_TYPE (method_type), func, arg_list);
TREE_SIDE_EFFECTS (call) = 1;
call = check_for_builtin (method, call);
tree
build_jni_stub (tree method)
{
- tree jnifunc, call, args, body, lookup_arg, method_sig, arg_types;
+ tree jnifunc, call, args, 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, res_type;
int args_size = 0;
tree klass = DECL_CONTEXT (method);
- int from_class = ! CLASS_FROM_SOURCE_P (klass);
klass = build_class_ref (klass);
gcc_assert (METHOD_NATIVE (method) && flag_jni);
DECL_ARTIFICIAL (method) = 1;
DECL_EXTERNAL (method) = 0;
- env_var = build_decl (VAR_DECL, get_identifier ("env"), ptr_type_node);
+ 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 (VAR_DECL, get_identifier ("res"),
+ res_var = build_decl (input_location, VAR_DECL, get_identifier ("res"),
TREE_TYPE (TREE_TYPE (method)));
DECL_CONTEXT (res_var) = method;
TREE_CHAIN (env_var) = res_var;
}
- meth_var = build_decl (VAR_DECL, get_identifier ("meth"), ptr_type_node);
- 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);
-
- /* One strange way that the front ends are different is that they
- store arguments differently. */
- if (from_class)
- method_args = DECL_ARGUMENTS (method);
- else
- method_args = BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (method));
+ method_args = DECL_ARGUMENTS (method);
block = build_block (env_var, NULL_TREE, method_args, NULL_TREE);
TREE_SIDE_EFFECTS (block) = 1;
- /* When compiling from source we don't set the type of the block,
- because that will prevent patch_return from ever being run. */
- if (from_class)
- TREE_TYPE (block) = TREE_TYPE (TREE_TYPE (method));
+ 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,
- build3 (CALL_EXPR, ptr_type_node,
- build_address_of (soft_getjnienvnewframe_node),
- build_tree_list (NULL_TREE, klass),
- NULL_TREE));
- CAN_COMPLETE_NORMALLY (body) = 1;
+ build_call_nary (ptr_type_node,
+ build_address_of (soft_getjnienvnewframe_node),
+ 1, klass));
/* All the arguments to this method become arguments to the
underlying JNI function. If we had to wrap object arguments in a
/* 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. */
- tem = build_tree_list (NULL_TREE, build_int_cst (NULL_TREE, args_size));
method_sig = build_java_signature (TREE_TYPE (method));
- lookup_arg = tree_cons (NULL_TREE,
- build_utf8_ref (unmangle_classname
- (IDENTIFIER_POINTER (method_sig),
- IDENTIFIER_LENGTH (method_sig))),
- tem);
- tem = DECL_NAME (method);
- lookup_arg
- = tree_cons (NULL_TREE, klass,
- tree_cons (NULL_TREE, build_utf8_ref (tem), lookup_arg));
-
+ 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
jni_func_type = build_pointer_type (tem);
- jnifunc = build3 (COND_EXPR, ptr_type_node,
- meth_var, meth_var,
- build2 (MODIFY_EXPR, ptr_type_node, meth_var,
- build3 (CALL_EXPR, ptr_type_node,
- build_address_of
- (soft_lookupjnimethod_node),
- lookup_arg, NULL_TREE)));
+ /* 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 = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (method)),
- build1 (NOP_EXPR, jni_func_type, jnifunc),
- args, NULL_TREE);
+ call = build_call_list (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 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 = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (method)),
- build_address_of (soft_unwrapjni_node),
- build_tree_list (NULL_TREE, call),
- NULL_TREE);
+ 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;
- CAN_COMPLETE_NORMALLY (call) = 1;
body = build2 (COMPOUND_EXPR, void_type_node, body, call);
TREE_SIDE_EFFECTS (body) = 1;
/* Now free the environment we allocated. */
- call = build3 (CALL_EXPR, ptr_type_node,
- build_address_of (soft_jnipopsystemframe_node),
- build_tree_list (NULL_TREE, env_var),
- NULL_TREE);
+ call = build_call_nary (ptr_type_node,
+ build_address_of (soft_jnipopsystemframe_node),
+ 1, env_var);
TREE_SIDE_EFFECTS (call) = 1;
- CAN_COMPLETE_NORMALLY (call) = 1;
body = build2 (COMPOUND_EXPR, void_type_node, body, call);
TREE_SIDE_EFFECTS (body) = 1;
}
body = build2 (COMPOUND_EXPR, void_type_node, body,
- build1 (RETURN_EXPR, res_type, res_var));
+ 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.
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_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
{
field_ref = build_field_ref (field_ref, self_type, field_name);
if (is_static
&& ! flag_indirect_dispatch)
- field_ref = build_class_init (self_type, field_ref);
+ {
+ 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 (DECL_CONTEXT (field_decl) != current_class)
error ("assignment to final field %q+D not in field's class",
field_decl);
- else if (FIELD_STATIC (field_decl))
- {
- if (!DECL_CLINIT_P (current_function_decl))
- warning (0, "assignment to final static field %q+D not in "
- "class initializer",
- field_decl);
- }
- else
- {
- tree cfndecl_name = DECL_NAME (current_function_decl);
- if (! DECL_CONSTRUCTOR_P (current_function_decl)
- && !ID_FINIT_P (cfndecl_name))
- warning (0, "assignment to final field %q+D not in constructor",
- field_decl);
- }
- }
- java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (field_ref),
- field_ref, new_value));
+ /* 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
- push_value (field_ref);
+ {
+ 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);
}
-void
-load_type_state (tree label)
+static void
+load_type_state (int pc)
{
int i;
- tree vec = LABEL_TYPE_STATE (label);
+ 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++)
JCF_SEEK (jcf, DECL_CODE_OFFSET (method));
byte_ops = jcf->read_ptr;
- instruction_bits = xrealloc (instruction_bits, length + 1);
+ 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 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));
return;
promote_arguments ();
+ cache_this_class_ref (method);
+ cache_cpool_data_ref ();
/* Translate bytecodes. */
linenumber_pointer = linenumber_table;
flush_quick_stack ();
if ((instruction_bits [PC] & BCODE_TARGET) != 0)
java_add_stmt (build1 (LABEL_EXPR, void_type_node, label));
- if (LABEL_VERIFIED (label) || PC == 0)
- load_type_state (label);
+ if ((instruction_bits[PC] & BCODE_VERIFIED) != 0)
+ load_type_state (PC);
}
if (! (instruction_bits [PC] & BCODE_VERIFIED))
if (pc == PC)
{
int line = GET_u2 (linenumber_pointer - 2);
-#ifdef USE_MAPPED_LOCATION
- input_location = linemap_line_start (&line_table, line, 1);
-#else
- input_location.line = line;
-#endif
+ 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;
}
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. */
warning (0, "unreachable bytecode from %d to the end of the method",
dead_code_index);
}
+
+ DECL_FUNCTION_LAST_LINE (method) = max_location;
}
static void
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);
}
#define ARRAY_NEW_MULTI() \
{ \
- tree class = get_class_constant (current_jcf, IMMEDIATE_u2 ); \
+ tree klass = get_class_constant (current_jcf, IMMEDIATE_u2 ); \
int ndims = IMMEDIATE_u1; \
- expand_java_multianewarray( class, ndims ); \
+ expand_java_multianewarray( klass, ndims ); \
}
#define UNOP(OPERAND_TYPE, OPERAND_VALUE) \
decl = find_local_variable (index, type, oldpc); \
set_local_type (index, type); \
java_add_stmt (build2 (MODIFY_EXPR, type, decl, value)); \
- update_aliases (decl, index, PC); \
}
#define STORE(OPERAND_TYPE, OPERAND_VALUE) \
if (flag_syntax_only)
return node;
if (TREE_CODE (node) == CALL_EXPR
- || TREE_CODE (node) == NEW_CLASS_EXPR
|| (TREE_CODE (node) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR
&& TREE_CODE (TREE_OPERAND (node, 1)) == SAVE_EXPR))
{
- tree arg, cmp;
+ tree call, cmp;
+ int i, nargs;
- arg = node;
-
- /* Position arg properly, account for wrapped around ctors. */
+ /* Account for wrapped around ctors. */
if (TREE_CODE (node) == COMPOUND_EXPR)
- arg = TREE_OPERAND (node, 0);
-
- arg = TREE_OPERAND (arg, 1);
-
- /* An empty argument list is ok, just ignore it. */
- if (!arg)
- return node;
+ call = TREE_OPERAND (node, 0);
+ else
+ call = node;
- /* Not having a list of arguments here is an error. */
- gcc_assert (TREE_CODE (arg) == TREE_LIST);
+ nargs = call_expr_nargs (call);
/* This reverses the evaluation order. This is a desired effect. */
- for (cmp = NULL_TREE; arg; arg = TREE_CHAIN (arg))
+ 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 (TREE_VALUE (arg));
+ 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)))
- TREE_VALUE (arg) = fold_convert (integer_type_node, TREE_VALUE (arg));
+ arg = fold_convert (integer_type_node, arg);
- saved = save_expr (force_evaluation_order (TREE_VALUE (arg)));
+ saved = save_expr (force_evaluation_order (arg));
cmp = (cmp == NULL_TREE ? saved :
build2 (COMPOUND_EXPR, void_type_node, cmp, saved));
- TREE_VALUE (arg) = saved;
+
+ CALL_EXPR_ARG (call, i) = saved;
}
if (cmp && TREE_CODE (cmp) == COMPOUND_EXPR)
cmp = build2 (COMPOUND_EXPR, TREE_TYPE (node), cmp, node);
if (TREE_TYPE (cmp) != void_type_node)
cmp = save_expr (cmp);
- CAN_COMPLETE_NORMALLY (cmp) = CAN_COMPLETE_NORMALLY (node);
TREE_SIDE_EFFECTS (cmp) = 1;
node = cmp;
}
return node;
}
-/* EXPR_WITH_FILE_LOCATION are used to keep track of the exact
- location where an expression or an identifier were encountered. It
- is necessary for languages where the frontend parser will handle
- recursively more than one file (Java is one of them). */
-
-tree
-build_expr_wfl (tree node,
-#ifdef USE_MAPPED_LOCATION
- source_location location
-#else
- const char *file, int line, int col
-#endif
-)
-{
- tree wfl;
-
-#ifdef USE_MAPPED_LOCATION
- wfl = make_node (EXPR_WITH_FILE_LOCATION);
- SET_EXPR_LOCATION (wfl, location);
-#else
- static const char *last_file = 0;
- static tree last_filenode = NULL_TREE;
-
- wfl = make_node (EXPR_WITH_FILE_LOCATION);
-
- EXPR_WFL_SET_LINECOL (wfl, line, col);
- if (file != last_file)
- {
- last_file = file;
- last_filenode = file ? get_identifier (file) : NULL_TREE;
- }
- EXPR_WFL_FILENAME_NODE (wfl) = last_filenode;
-#endif
- EXPR_WFL_NODE (wfl) = node;
- if (node)
- {
- if (!TYPE_P (node))
- TREE_SIDE_EFFECTS (wfl) = TREE_SIDE_EFFECTS (node);
- TREE_TYPE (wfl) = TREE_TYPE (node);
- }
-
- return wfl;
-}
-
-#ifdef USE_MAPPED_LOCATION
-tree
-expr_add_location (tree node, source_location location, bool statement)
-{
- tree wfl;
-#if 0
- /* FIXME. This optimization causes failures in code that expects an
- EXPR_WITH_FILE_LOCATION. E.g. in resolve_qualified_expression_name. */
- if (node && ! (statement && flag_emit_class_files))
- {
- source_location node_loc = EXPR_LOCATION (node);
- if (node_loc == location || location == UNKNOWN_LOCATION)
- return node;
- if (node_loc == UNKNOWN_LOCATION
- && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (node))))
- {
- SET_EXPR_LOCATION (node, location);
- return node;
- }
- }
-#endif
- wfl = make_node (EXPR_WITH_FILE_LOCATION);
- SET_EXPR_LOCATION (wfl, location);
- EXPR_WFL_NODE (wfl) = node;
- if (statement && debug_info_level != DINFO_LEVEL_NONE)
- EXPR_WFL_EMIT_LINE_NOTE (wfl) = 1;
- if (node)
- {
- if (!TYPE_P (node))
- TREE_SIDE_EFFECTS (wfl) = TREE_SIDE_EFFECTS (node);
- TREE_TYPE (wfl) = TREE_TYPE (node);
- }
-
- return wfl;
-}
-#endif
-
/* Build a node to represent empty statements and blocks. */
tree
build_java_empty_stmt (void)
{
- tree t = build_empty_stmt ();
- CAN_COMPLETE_NORMALLY (t) = 1;
+ tree t = build_empty_stmt (input_location);
return t;
}
}
}
+/* 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"