+2002-02-28 Bryce McKinlay <bryce@waitaki.otago.ac.nz>
+
+ * expr.c (java_array_data_offset): Removed function.
+ (JAVA_ARRAY_LENGTH_OFFSET): Removed macro.
+ (build_java_array_length_access): Obtain "length" value using a
+ COMPONENT_REF, instead of INDIRECT_REF and arithmetic.
+ (build_java_arrayaccess): Correct comment. Access "data" using a
+ COMPONENT_REF, and return an ARRAY_REF instead of an INDIRECT_REF.
+ (build_java_arraystore_check): New function.
+ (expand_java_arraystore): Use build_java_arraystore_check.
+ * parse.y (patch_assignment): Simplify code to insert a store check
+ when lvalue is an ARRAY_REF. Use build_java_arraystore_check.
+ * check-init.c (check_init): Update to reflect that an array length
+ access is now a COMPONENT_REF.
+ * gcj.texi (Code Generation): Improve documentation of
+ -fno-bounds-check. Add documentation for -fno-store-check.
+ * java-tree.h (flag_store_check): Declare.
+ (build_java_arraystore_check): Declare.
+ * lang.c (flag_store_check): Initialize to 1.
+ (lang_f_options): Add store-check option.
+ * jvspec.c: Don't pass store-check option to jvgenmain.
+ * lang-options.h: Add help string for -fno-store-check.
+
2002-02-28 Neil Booth <neil@daikokuya.demon.co.uk>
* decl.c (copy_lang_decl): Rename java_dup_lang_specific_decl.
2002-02-22 Per Bothner <per@bothner.com>
- * class.c: Change vtable to be more compatible with g++ v3 abi.
+ * class.c: Change vtable to be more compatible with g++ v3 abi.
(get_dispatch_table): Prepend offset-to-top (always 0) and
type_info pointer (currently unimplemented hence NULL) to vtable.
Specifically, prepend offset-to-top and typeinfo ptr (currently null).
final_assign_error (DECL_NAME (decl));
break;
}
- else if (TREE_CODE (tmp) == INDIRECT_REF && IS_ARRAY_LENGTH_ACCESS (tmp))
+ else if (TREE_CODE (tmp) == COMPONENT_REF && IS_ARRAY_LENGTH_ACCESS (tmp))
{
/* We can't emit a more specific message here, because when
compiling to bytecodes we don't get here. */
static void java_stack_pop PARAMS ((int));
static tree build_java_throw_out_of_bounds_exception PARAMS ((tree));
static tree build_java_check_indexed_type PARAMS ((tree, tree));
-static tree java_array_data_offset PARAMS ((tree));
static tree case_identity PARAMS ((tree, tree));
static unsigned char peek_opcode_at_pc PARAMS ((struct JCF *, int, int));
static bool emit_init_test_initialization PARAMS ((struct hash_entry *,
/* Implementation of operations on array: new, load, store, length */
-/* Array core info access macros */
-
-#define JAVA_ARRAY_LENGTH_OFFSET(A) \
- byte_position (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (TREE_TYPE (A)))))
-
tree
decode_newarray_type (atype)
int atype;
tree node;
{
tree type = TREE_TYPE (node);
+ tree array_type = TREE_TYPE (type);
HOST_WIDE_INT length;
if (!is_array_type_p (type))
length = java_array_type_length (type);
if (length >= 0)
return build_int_2 (length, 0);
- node = build1 (INDIRECT_REF, int_type_node,
- fold (build (PLUS_EXPR, ptr_type_node,
- java_check_reference (node,
- flag_check_references),
- JAVA_ARRAY_LENGTH_OFFSET(node))));
+
+ node = build (COMPONENT_REF, int_type_node,
+ build_java_indirect_ref (array_type, node,
+ flag_check_references),
+ lookup_field (&array_type, get_identifier ("length")));
IS_ARRAY_LENGTH_ACCESS (node) = 1;
- return fold (node);
+ return node;
}
/* Optionally checks a reference against the NULL pointer. ARG1: the
return build1 (INDIRECT_REF, type, java_check_reference (expr, check));
}
-static tree
-java_array_data_offset (array)
- tree array;
-{
- tree array_type = TREE_TYPE (TREE_TYPE (array));
- tree data_fld = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (array_type)));
-
- if (data_fld == NULL_TREE)
- return size_in_bytes (array_type);
- else
- return byte_position (data_fld);
-}
-
/* 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.
build_java_arrayaccess (array, type, index)
tree array, type, index;
{
- tree arith, node, throw = NULL_TREE;
-
- arith = fold (build (PLUS_EXPR, int_type_node,
- java_array_data_offset (array),
- fold (build (MULT_EXPR, int_type_node,
- index, size_in_bytes(type)))));
+ tree node, throw = NULL_TREE;
+ tree data_field;
+ tree ref;
+ tree array_type = TREE_TYPE (TREE_TYPE (array));
if (flag_bounds_check)
{
}
}
- /* The SAVE_EXPR is for correct evaluation order. It would be
- cleaner to use force_evaluation_order (see comment there), but
- that is difficult when we also have to deal with bounds
- checking. The SAVE_EXPR is not necessary to do that when we're
- not checking for array bounds. */
- if (TREE_SIDE_EFFECTS (index) && throw)
- throw = build (COMPOUND_EXPR, int_type_node, save_expr (array), throw);
-
- node = build1 (INDIRECT_REF, type,
- fold (build (PLUS_EXPR, ptr_type_node,
- java_check_reference (array,
- flag_check_references),
- (throw ? build (COMPOUND_EXPR, int_type_node,
- throw, arith ) : arith))));
+ /* 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 = build (COMPOUND_EXPR, int_type_node, throw, index);
+
+ data_field = lookup_field (&array_type, get_identifier ("data"));
+
+ ref = build (COMPONENT_REF, TREE_TYPE (data_field),
+ build_java_indirect_ref (array_type, array,
+ flag_check_references),
+ data_field);
+
+ node = build (ARRAY_REF, type, ref, index);
return 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 (array, object)
+ tree array;
+ tree object;
+{
+ tree check, element_type;
+ tree array_type_p = TREE_TYPE (array);
+ tree object_type = TYPE_NAME (TREE_TYPE (TREE_TYPE (object)));
+
+ if (! is_array_type_p (array_type_p))
+ abort ();
+
+ /* Get the TYPE_DECL for ARRAY's element type. */
+ element_type = TYPE_NAME (TREE_TYPE (TREE_TYPE (TREE_TYPE (array_type_p))));
+
+ if (TREE_CODE (element_type) != TYPE_DECL
+ || TREE_CODE (object_type) != TYPE_DECL)
+ abort ();
+
+ if (!flag_store_check)
+ return build1 (NOP_EXPR, array_type_p, array);
+
+ /* No check is needed if the element type is final or is itself an array.
+ Also check that element_type matches object_type, since in the bytecode
+ compilation case element_type may be the actual element type of the arra
+ rather than its declared type. */
+ if (element_type == object_type
+ && (TYPE_ARRAY_P (TREE_TYPE (element_type))
+ || CLASS_FINAL (element_type)))
+ return build1 (NOP_EXPR, array_type_p, array);
+
+ /* Avoid the check if OBJECT was just loaded from the same array. */
+ if (TREE_CODE (object) == ARRAY_REF)
+ {
+ tree target;
+ tree source = TREE_OPERAND (object, 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_EXPR, void_type_node,
+ build_address_of (soft_checkarraystore_node),
+ tree_cons (NULL_TREE, array,
+ build_tree_list (NULL_TREE, object)),
+ NULL_TREE);
+ 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>.
if (TREE_CODE (rhs_type_node) == POINTER_TYPE)
{
- tree check = build (CALL_EXPR, void_type_node,
- build_address_of (soft_checkarraystore_node),
- tree_cons (NULL_TREE, array,
- build_tree_list (NULL_TREE, rhs_node)),
- NULL_TREE);
- TREE_SIDE_EFFECTS (check) = 1;
+ tree check = build_java_arraystore_check (array, rhs_node);
expand_expr_stmt (check);
}
@item -fno-bounds-check
By default, @code{gcj} generates code which checks the bounds of all
-array indexing operations. With this option, these checks are omitted.
-Note that this can result in unpredictable behavior if the code in
-question actually does violate array bounds constraints.
+array indexing operations. With this option, these checks are omitted, which
+can improve performance for code that uses arrays extensively. Note that this
+can result in unpredictable behavior if the code in question actually does
+violate array bounds constraints. It is safe to use this option if you are
+sure that your code will never throw an @code{ArrayIndexOutOfBoundsException}.
+
+@item -fno-store-check
+Don't generate array store checks. When storing objects into arrays, a runtime
+check is normally generated in order to ensure that the object is assignment
+compatible with the component type of the array (which may not be known
+at compile-time). With this option, these checks are omitted. This can
+improve performance for code which stores objects into arrays frequently.
+It is safe to use this option if you are sure your code will never throw an
+@code{ArrayStoreException}.
@item -fjni
With @code{gcj} there are two options for writing native methods: CNI
in order to improve binary compatibility. */
extern int flag_indirect_dispatch;
+/* When zero, don't generate runtime array store checks. */
+extern int flag_store_check;
+
/* Encoding used for source files. */
extern const char *current_encoding;
extern tree build_java_soft_divmod PARAMS ((enum tree_code, tree, tree, tree));
extern tree binary_numeric_promotion PARAMS ((tree, tree, tree *, tree *));
extern tree build_java_arrayaccess PARAMS ((tree, tree, tree));
+extern tree build_java_arraystore_check PARAMS ((tree, tree));
extern tree build_newarray PARAMS ((int, tree));
extern tree build_anewarray PARAMS ((tree, tree));
extern tree build_new_array PARAMS ((tree, tree));
%{<femit-class-file} %{<femit-class-files} %{<fencoding*}\
%{<fuse-boehm-gc} %{<fhash-synchronization} %{<fjni}\
%{<findirect-dispatch} \
+ %{<fno-store-check}\
%{<fclasspath*} %{<fCLASSPATH*} %{<foutput-class-dir}\
%{<fuse-divide-subroutine} %{<fno-use-divide-subroutine}\
%{<fcheck-references} %{<fno-check-references}\
{ "-fbounds-check", "" },
{ "-fno-bounds-check",
N_("Disable automatic array bounds checking") },
+ { "-fno-store-check",
+ N_("Disable assignability checks for stores into object arrays") },
{ "-fjni",
N_("Assume native functions are implemented using JNI") },
{ "--CLASSPATH",
N_("Always check for non gcj generated classes archives") },
{ "-fno-optimize-static-class-initialization",
N_("Never optimize static class initialization code") },
+ { "-findirect-dispatch",
+ N_("Use offset tables for virtual method calls") },
in order to improve binary compatibility. */
int flag_indirect_dispatch = 0;
+/* When zero, don't generate runtime array store checks. */
+int flag_store_check = 1;
+
/* When non zero, print extra version information. */
static int version_flag = 0;
{"check-references", &flag_check_references, 1},
{"force-classes-archive-check", &flag_force_classes_archive_check, 1},
{"optimize-static-class-initialization", &flag_optimize_sci, 1 },
- {"indirect-dispatch", &flag_indirect_dispatch, 1}
+ {"indirect-dispatch", &flag_indirect_dispatch, 1},
+ {"store-check", &flag_store_check, 1}
};
static const struct string_option
{
lhs_type = TREE_TYPE (lvalue);
}
- /* Or Lhs can be a array access. Should that be lvalue ? FIXME +
- comment on reason why */
- else if (TREE_CODE (wfl_op1) == ARRAY_REF)
+ /* Or Lhs can be an array access. */
+ else if (TREE_CODE (lvalue) == ARRAY_REF)
{
lhs_type = TREE_TYPE (lvalue);
lvalue_from_array = 1;
&& lvalue_from_array
&& JREFERENCE_TYPE_P (TYPE_ARRAY_ELEMENT (lhs_type)))
{
- tree check;
- tree base = lvalue;
+ tree array, store_check, base, index_expr;
- /* We need to retrieve the right argument for
- _Jv_CheckArrayStore. This is somewhat complicated by bounds
- and null pointer checks, both of which wrap the operand in
- one layer of COMPOUND_EXPR. */
- if (TREE_CODE (lvalue) == COMPOUND_EXPR)
- base = TREE_OPERAND (lvalue, 0);
- else
+ /* Get the INDIRECT_REF. */
+ array = TREE_OPERAND (TREE_OPERAND (lvalue, 0), 0);
+ /* Get the array pointer expr. */
+ array = TREE_OPERAND (array, 0);
+ store_check = build_java_arraystore_check (array, new_rhs);
+
+ index_expr = TREE_OPERAND (lvalue, 1);
+
+ if (TREE_CODE (index_expr) == COMPOUND_EXPR)
{
- tree op = TREE_OPERAND (base, 0);
-
- /* We can have a SAVE_EXPR here when doing String +=. */
- if (TREE_CODE (op) == SAVE_EXPR)
- op = TREE_OPERAND (op, 0);
- /* We can have a COMPOUND_EXPR here when doing bounds check. */
- if (TREE_CODE (op) == COMPOUND_EXPR)
- op = TREE_OPERAND (op, 1);
- base = TREE_OPERAND (op, 0);
- /* Strip the last PLUS_EXPR to obtain the base. */
- if (TREE_CODE (base) == PLUS_EXPR)
- base = TREE_OPERAND (base, 0);
- }
-
- /* Build the invocation of _Jv_CheckArrayStore */
- new_rhs = save_expr (new_rhs);
- check = build (CALL_EXPR, void_type_node,
- build_address_of (soft_checkarraystore_node),
- tree_cons (NULL_TREE, base,
- build_tree_list (NULL_TREE, new_rhs)),
- NULL_TREE);
- TREE_SIDE_EFFECTS (check) = 1;
-
- /* We have to decide on an insertion point */
- if (TREE_CODE (lvalue) == COMPOUND_EXPR)
- {
- tree t;
- if (flag_bounds_check)
- {
- t = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (lvalue, 1), 0), 0);
- TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (lvalue, 1), 0), 0) =
- build (COMPOUND_EXPR, void_type_node, t, check);
- }
- else
- TREE_OPERAND (lvalue, 1) = build (COMPOUND_EXPR, lhs_type,
- check, TREE_OPERAND (lvalue, 1));
+ /* A COMPOUND_EXPR here is a bounds check. The bounds check must
+ happen before the store check, so prepare to insert the store
+ check within the second operand of the existing COMPOUND_EXPR. */
+ base = index_expr;
}
- else if (flag_bounds_check)
- {
- tree hook = lvalue;
- tree compound = TREE_OPERAND (lvalue, 0);
- tree bound_check, new_compound;
-
- if (TREE_CODE (compound) == SAVE_EXPR)
- {
- compound = TREE_OPERAND (compound, 0);
- hook = TREE_OPERAND (hook, 0);
- }
-
- /* Find the array bound check, hook the original array access. */
- bound_check = TREE_OPERAND (compound, 0);
- TREE_OPERAND (hook, 0) = TREE_OPERAND (compound, 1);
-
- /* Make sure the bound check will happen before the store check */
- new_compound =
- build (COMPOUND_EXPR, void_type_node, bound_check, check);
-
- /* Re-assemble the augmented array access. */
- lvalue = build (COMPOUND_EXPR, TREE_TYPE (lvalue),
- new_compound, lvalue);
- }
else
- lvalue = build (COMPOUND_EXPR, TREE_TYPE (lvalue), check, lvalue);
+ base = lvalue;
+
+ index_expr = TREE_OPERAND (base, 1);
+ TREE_OPERAND (base, 1) = build (COMPOUND_EXPR, TREE_TYPE (index_expr),
+ store_check, index_expr);
}
/* Final locals can be used as case values in switch