X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fjava%2Fexpr.c;h=abc5f69103fffb4fb45b83ec93b1837f1663ae33;hb=3e02ce3031888cf1bff6c5ee5267510f5874a01a;hp=7ac3d19c7ec774227a0e53a5619ab54a1d88655c;hpb=7523afefd027644170ddafcbf0f5112db2f40658;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/java/expr.c b/gcc/java/expr.c index 7ac3d19c7ec..abc5f69103f 100644 --- a/gcc/java/expr.c +++ b/gcc/java/expr.c @@ -1,5 +1,5 @@ /* Process expressions for the GNU compiler for the Java(TM) language. - Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. This file is part of GNU CC. @@ -29,6 +29,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "tree.h" #include "real.h" #include "rtl.h" +#include "flags.h" #include "expr.h" #include "java-tree.h" #include "javaop.h" @@ -37,19 +38,60 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "java-except.h" #include "parse.h" #include "toplev.h" +#include "except.h" +#include "defaults.h" +#include "ggc.h" + +static void flush_quick_stack PARAMS ((void)); +static void push_value PARAMS ((tree)); +static tree pop_value PARAMS ((tree)); +static void java_stack_swap PARAMS ((void)); +static void java_stack_dup PARAMS ((int, int)); +static void build_java_athrow PARAMS ((tree)); +static void build_java_jsr PARAMS ((tree, tree)); +static void build_java_ret PARAMS ((tree)); +static void expand_java_multianewarray PARAMS ((tree, int)); +static void expand_java_arraystore PARAMS ((tree)); +static void expand_java_arrayload PARAMS ((tree)); +static void expand_java_array_length PARAMS ((void)); +static tree build_java_monitor PARAMS ((tree, tree)); +static void expand_java_pushc PARAMS ((int, tree)); +static void expand_java_return PARAMS ((tree)); +static void expand_java_NEW PARAMS ((tree)); +static void expand_java_INSTANCEOF PARAMS ((tree)); +static void expand_java_CHECKCAST PARAMS ((tree)); +static void expand_iinc PARAMS ((unsigned int, int, int)); +static void expand_java_binop PARAMS ((tree, enum tree_code)); +static void note_label PARAMS ((int, int)); +static void expand_compare PARAMS ((enum tree_code, tree, tree, int)); +static void expand_test PARAMS ((enum tree_code, tree, int)); +static void expand_cond PARAMS ((enum tree_code, tree, int)); +static void expand_java_goto PARAMS ((int)); +#if 0 +static void expand_java_call PARAMS ((int, int)); +static void expand_java_ret PARAMS ((tree)); +#endif +static tree pop_arguments PARAMS ((tree)); +static void expand_invoke PARAMS ((int, int, int)); +static void expand_java_field_op PARAMS ((int, int, int)); +static void java_push_constant_from_pool PARAMS ((struct JCF *, int)); +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 tree operand_type[59]; extern struct obstack permanent_obstack; -void -init_expr_processing() -{ - 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; -} +static tree methods_ident = NULL_TREE; +static tree ncode_ident = NULL_TREE; +tree dtable_ident = NULL_TREE; + +/* Set to non-zero value in order to emit class initilization code + before static field references. */ +int always_initialize_class_p; /* We store the stack state in two places: Within a basic block, we use the quick_stack, which is a @@ -78,19 +120,34 @@ init_expr_processing() So dup cannot just add an extra element to the quick_stack, but iadd can. */ -tree quick_stack = NULL_TREE; +static tree quick_stack = NULL_TREE; /* A free-list of unused permamnet TREE_LIST nodes. */ -tree tree_list_free_list = NULL_TREE; +static tree tree_list_free_list = NULL_TREE; /* The stack pointer of the Java virtual machine. This does include the size of the quick_stack. */ int stack_pointer; -unsigned char *linenumber_table; +const unsigned char *linenumber_table; int linenumber_count; +void +init_expr_processing() +{ + 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; + ggc_add_tree_root (operand_type, 59); + ggc_add_tree_root (&methods_ident, 1); + ggc_add_tree_root (&ncode_ident, 1); + ggc_add_tree_root (&quick_stack, 1); + ggc_add_tree_root (&tree_list_free_list, 1); +} + tree truthvalue_conversion (expr) tree expr; @@ -166,7 +223,7 @@ unhand_expr (expr) that the expression for a slot may contain decls for stack slots with higher (or the same) index, but not lower. */ -void +static void flush_quick_stack () { int stack_index = stack_pointer; @@ -199,22 +256,34 @@ flush_quick_stack () } } -void -push_type (type) +/* Push TYPE on the type stack. + Return true on success, 0 on overflow. */ + +int +push_type_0 (type) 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)) - fatal ("stack overflow"); + return 0; stack_type_map[stack_pointer++] = type; n_words--; while (--n_words >= 0) stack_type_map[stack_pointer++] = TYPE_SECOND; + return 1; } void +push_type (type) + tree type; +{ + if (! push_type_0 (type)) + fatal ("stack overflow"); +} + +static void push_value (value) tree value; { @@ -226,7 +295,7 @@ push_value (value) } push_type (type); if (tree_list_free_list == NULL_TREE) - quick_stack = perm_tree_cons (NULL_TREE, value, quick_stack); + quick_stack = tree_cons (NULL_TREE, value, quick_stack); else { tree node = tree_list_free_list; @@ -237,22 +306,34 @@ push_value (value) } } +/* 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 (type) +pop_type_0 (type, messagep) tree type; + char **messagep; { int n_words; - int i; 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) - fatal ("stack underflow"); + { + *messagep = xstrdup ("stack underflow"); + return type; + } while (--n_words > 0) { if (stack_type_map[--stack_pointer] != void_type_node) - fatal ("Invalid multi-word value on type stack"); + { + *messagep = xstrdup ("Invalid multi-word value on type stack"); + return type; + } } t = stack_type_map[--stack_pointer]; if (type == NULL_TREE || t == type) @@ -271,12 +352,45 @@ pop_type (type) /* This is a kludge, but matches what Sun's verifier does. It can be tricked, but is safe as long as type errors (i.e. interface method calls) are caught at run-time. */ - else if (CLASS_INTERFACE (TYPE_NAME (TREE_TYPE (type))) - && t == object_ptr_type_node) - return t; + /* FIXME: this is worse than a kludge, probably. */ + return object_ptr_type_node; } - error ("unexpected type on stack"); - return t; + { + const char *str1 = "expected type '"; + const char *str3 = "' but stack contains '"; + const char *str5 = "'"; + int len1 = strlen (str1); + int len2 = strlen (lang_printable_name (type, 0)); + int len3 = strlen (str3); + int len4 = strlen (lang_printable_name (t, 0)); + int len5 = strlen (str5); + char *msg = xmalloc (len1 + len2 + len3 + len4 + len5 + 1); + *messagep = msg; + strcpy (msg, str1); msg += len1; + strcpy (msg, lang_printable_name (type, 0)); msg += len2; + strcpy (msg, str3); msg += len3; + strcpy (msg, lang_printable_name (t, 0)); msg += len4; + strcpy (msg, str5); + 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 (type) + tree type; +{ + char *message = NULL; + type = pop_type_0 (type, &message); + if (message != NULL) + { + error (message); + free (message); + } + return type; } /* Return 1f if SOURCE_TYPE can be safely widened to TARGET_TYPE. @@ -327,6 +441,10 @@ can_widen_reference_to (source_type, target_type) int source_depth = class_depth (source_type); int target_depth = class_depth (target_type); + /* 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 @@ -339,8 +457,8 @@ can_widen_reference_to (source_type, target_type) (TREE_TYPE (TREE_VEC_ELT (basetype_vec, i)), target_type)) return 1; - if (n == 0) - return 0; + if (n == 0) + return 0; } for ( ; source_depth > target_depth; source_depth--) @@ -352,12 +470,10 @@ can_widen_reference_to (source_type, target_type) } } -tree +static tree pop_value (type) tree type; { - int n_words = 1 + TYPE_IS_WIDE (type); - int i; type = pop_type (type); if (quick_stack) { @@ -375,7 +491,7 @@ pop_value (type) /* Pop and discrad the top COUNT stack slots. */ -void +static void java_stack_pop (count) int count; { @@ -399,7 +515,7 @@ java_stack_pop (count) /* Implement the 'swap' operator (to swap two top stack slots). */ -void +static void java_stack_swap () { tree type1, type2; @@ -423,7 +539,7 @@ java_stack_swap () stack_type_map[stack_pointer - 2] = type1; } -void +static void java_stack_dup (size, offset) int size, offset; { @@ -466,9 +582,10 @@ java_stack_dup (size, offset) } } -/* Calls _Jv_Throw. Discard the contents of the value stack. */ +/* Calls _Jv_Throw or _Jv_Sjlj_Throw. Discard the contents of the + value stack. */ -tree +static void build_java_athrow (node) tree node; { @@ -476,7 +593,7 @@ build_java_athrow (node) call = build (CALL_EXPR, void_type_node, - build_address_of (throw_node), + build_address_of (throw_node[exceptions_via_longjmp ? 1 : 0]), build_tree_list (NULL_TREE, node), NULL_TREE); TREE_SIDE_EFFECTS (call) = 1; @@ -486,7 +603,7 @@ build_java_athrow (node) /* Implementation for jsr/ret */ -void +static void build_java_jsr (where, ret) tree where; tree ret; @@ -494,11 +611,11 @@ build_java_jsr (where, ret) tree ret_label = fold (build1 (ADDR_EXPR, return_address_type_node, ret)); push_value (ret_label); flush_quick_stack (); - expand_goto (where); + emit_jump (label_rtx (where)); expand_label (ret); } -void +static void build_java_ret (location) tree location; { @@ -509,14 +626,12 @@ build_java_ret (location) /* Array core info access macros */ -#define JAVA_ARRAY_LENGTH_OFFSET(A) \ - size_binop (CEIL_DIV_EXPR, \ - (DECL_FIELD_BITPOS \ - (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (TREE_TYPE (A)))))), \ - size_int (BITS_PER_UNIT)) +#define JAVA_ARRAY_LENGTH_OFFSET(A) \ + byte_position (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (TREE_TYPE (A))))) tree -decode_newarray_type (int atype) +decode_newarray_type (atype) + int atype; { switch (atype) { @@ -532,6 +647,32 @@ decode_newarray_type (int atype) } } +/* Map primitive type to the code used by OPCODE_newarray. */ + +int +encode_newarray_type (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 + fatal ("Can't compute type code - patch_newarray"); +} + /* Build a call to _Jv_ThrowBadArrayIndex(), the ArrayIndexOfBoundsException exception handler. */ @@ -574,9 +715,9 @@ build_java_array_length_access (node) tree build_java_arraynull_check (node, expr, type) - tree node; - tree expr; - tree type; + tree node ATTRIBUTE_UNUSED; + tree expr; + tree type ATTRIBUTE_UNUSED; { #if 0 static int java_array_access_throws_null_exception = 0; @@ -597,11 +738,11 @@ java_array_data_offset (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 build_int_2 (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (data_fld)) - / BITS_PER_UNIT, 0); + return byte_position (data_fld); } /* Implement array indexing (either as l-value or r-value). @@ -684,39 +825,52 @@ build_java_check_indexed_type (array_node, indexed_type) return indexed_type; } -/* newarray triggers a call to _Jv_NewArray. This function should be called - with an integer code (the type of array to create) and get from the stack - the size of the dimmension. */ +/* 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 (atype_value, length) int atype_value; tree length; { - tree type = build_java_array_type (decode_newarray_type (atype_value), - TREE_CODE (length) == INTEGER_CST - ? TREE_INT_CST_LOW (length) - : -1); + 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); + + /* 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_2 (atype_value, 0); + else + type_arg = build_class_ref (prim_type); + return build (CALL_EXPR, promote_type (type), build_address_of (soft_newarray_node), tree_cons (NULL_TREE, - build_int_2 (atype_value, 0), + type_arg, build_tree_list (NULL_TREE, length)), NULL_TREE); } /* Generates anewarray from a given CLASS_TYPE. Gets from the stack the size of the dimension. */ -/* Merge with build_newarray. FIXME. */ + tree build_anewarray (class_type, length) tree class_type; tree length; { - tree type = build_java_array_type (class_type, - TREE_CODE (length) == INTEGER_CST - ? TREE_INT_CST_LOW (length) - : -1); + tree type + = build_java_array_type (class_type, + host_integerp (length, 0) + ? tree_low_cst (length, 0) : -1); + return build (CALL_EXPR, promote_type (type), build_address_of (soft_anewarray_node), tree_cons (NULL_TREE, length, @@ -726,11 +880,24 @@ build_anewarray (class_type, length) NULL_TREE); } +/* Return a node the evaluates 'new TYPE[LENGTH]'. */ + +tree +build_new_array (type, length) + 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. */ -void +static void expand_java_multianewarray (class_type, ndim) tree class_type; int ndim; @@ -760,7 +927,7 @@ expand_java_multianewarray (class_type, ndim) 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. */ -void +static void expand_java_arraystore (rhs_type_node) tree rhs_type_node; { @@ -777,8 +944,7 @@ expand_java_arraystore (rhs_type_node) index = save_expr (index); array = save_expr (array); - if (TREE_CODE (rhs_type_node) == POINTER_TYPE - && !CLASS_FINAL (TYPE_NAME (TREE_TYPE (rhs_type_node)))) + if (TREE_CODE (rhs_type_node) == POINTER_TYPE) { tree check = build (CALL_EXPR, void_type_node, build_address_of (soft_checkarraystore_node), @@ -802,12 +968,11 @@ expand_java_arraystore (rhs_type_node) BOOLEAN/SHORT, we push a promoted type back to the stack. */ -void +static void expand_java_arrayload (lhs_type_node ) tree lhs_type_node; { tree load_node; - int convert; tree index_node = pop_value (int_type_node); tree array_node = pop_value (ptr_type_node); @@ -827,7 +992,7 @@ expand_java_arrayload (lhs_type_node ) /* Expands .length. Makes sure that we deal with and array and may expand a NULL check on the array object. */ -void +static void expand_java_array_length () { tree array = pop_value (ptr_type_node); @@ -839,7 +1004,7 @@ expand_java_array_length () /* Emit code for the call to _Jv_Monitor{Enter,Exit}. CALL can be either soft_monitorenter_node or soft_monitorexit_node. */ -tree +static tree build_java_monitor (call, object) tree call; tree object; @@ -853,7 +1018,7 @@ build_java_monitor (call, object) /* Emit code for one of the PUSHC instructions. */ -void +static void expand_java_pushc (ival, type) int ival; tree type; @@ -881,7 +1046,7 @@ expand_java_pushc (ival, type) push_value (value); } -void +static void expand_java_return (type) tree type; { @@ -892,6 +1057,16 @@ expand_java_return (type) tree retval = pop_value (type); tree res = DECL_RESULT (current_function_decl); retval = build (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; expand_return (retval); } @@ -904,13 +1079,13 @@ build_address_of (value) return build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (value)), value); } -void +static void expand_java_NEW (type) tree type; { if (! CLASS_LOADED_P (type)) load_class (type, 1); - layout_class_methods (type); + safe_layout_class (type); push_value (build (CALL_EXPR, promote_type (type), build_address_of (alloc_object_node), tree_cons (NULL_TREE, build_class_ref (type), @@ -919,21 +1094,102 @@ expand_java_NEW (type) NULL_TREE)); } -void +/* This returns an expression which will extract the class of an + object. */ + +tree +build_get_class (value) + tree value; +{ + tree class_field = lookup_field (&dtable_type, get_identifier ("class")); + tree vtable_field = lookup_field (&object_type_node, + get_identifier ("vtable")); + return build (COMPONENT_REF, class_ptr_type, + build1 (INDIRECT_REF, dtable_type, + build (COMPONENT_REF, dtable_ptr_type, + build1 (INDIRECT_REF, object_type_node, value), + vtable_field)), + class_field); +} + +/* 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 (value, type) + tree value, 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 = build (COND_EXPR, itype, + value, + boolean_true_node, boolean_false_node); + } + else if (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 = build (COND_EXPR, itype, + save, + build (EQ_EXPR, itype, + build_get_class (save), + build_class_ref (type)), + boolean_false_node); + } + else + { + expr = build (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); + } + TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (value); + return expr; +} + +static void expand_java_INSTANCEOF (type) tree type; { tree value = pop_value (object_ptr_type_node); - value = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (soft_instanceof_node)), - build_address_of (soft_instanceof_node), - tree_cons (NULL_TREE, value, - build_tree_list (NULL_TREE, - build_class_ref (type))), - NULL_TREE); + value = build_instanceof (value, type); push_value (value); } -void +static void expand_java_CHECKCAST (type) tree type; { @@ -946,8 +1202,11 @@ expand_java_CHECKCAST (type) push_value (value); } -void -expand_iinc (unsigned int local_var_index, int ival, int pc) +static void +expand_iinc (local_var_index, ival, pc) + unsigned int local_var_index; + int ival; + int pc; { tree local_var, res; tree constant_value; @@ -959,6 +1218,57 @@ expand_iinc (unsigned int local_var_index, int ival, int pc) expand_assignment (local_var, res, 0, 0); } + +tree +build_java_soft_divmod (op, type, op1, op2) + enum tree_code op; + tree type, op1, 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; + } + } + + if (! call) + fatal ("Internal compiler error in build_java_soft_divmod"); + + call = build (CALL_EXPR, type, + build_address_of (call), + tree_cons (NULL_TREE, arg1, + build_tree_list (NULL_TREE, arg2)), + NULL_TREE); + + return call; +} + tree build_java_binop (op, type, arg1, arg2) enum tree_code op; @@ -1007,10 +1317,11 @@ build_java_binop (op, type, arg1, arg2) integer_zero_node)); return fold (build (COND_EXPR, int_type_node, ifexp1, integer_negative_one_node, second_compare)); - } - + } + case TRUNC_DIV_EXPR: case TRUNC_MOD_EXPR: - if (TREE_CODE (type) == REAL_TYPE) + if (TREE_CODE (type) == REAL_TYPE + && op == TRUNC_MOD_EXPR) { tree call; if (type != double_type_node) @@ -1027,29 +1338,19 @@ build_java_binop (op, type, arg1, arg2) 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; - -#if 0 /* not required */ - case PLUS_EXPR: - case MULT_EXPR: - case MINUS_EXPR: - case TRUNC_DIV_EXPR: - case RDIV_EXPR: -/* case REM_EXPR: */ - case BIT_AND_EXPR: - case BIT_IOR_EXPR: - case BIT_XOR_EXPR: - break; - default: - error ("unknown opcode"); - return error_mark_node; -#endif - + default: ; } return fold (build (op, type, arg1, arg2)); } -void +static void expand_java_binop (type, op) tree type; enum tree_code op; { @@ -1073,7 +1374,8 @@ expand_java_binop (type, op) /* Lookup the field named NAME in *TYPEP or its super classes. If not found, return NULL_TREE. - (If the *TYPEP is not found, return error_mark_node.) + (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. */ @@ -1085,17 +1387,59 @@ lookup_field (typep, name) if (CLASS_P (*typep) && !CLASS_LOADED_P (*typep)) { load_class (*typep, 1); - if (TREE_CODE (TYPE_SIZE (*typep)) == ERROR_MARK) + safe_layout_class (*typep); + if (!TYPE_SIZE (*typep) || TREE_CODE (TYPE_SIZE (*typep)) == ERROR_MARK) return error_mark_node; } do { - tree field; - for (field = TYPE_FIELDS (*typep); field; field = TREE_CHAIN (field)) + tree field, basetype_vec; + tree save_field; + int n, i; + + for (field = TYPE_FIELDS (*typep); field; field = TREE_CHAIN (field)) + if (DECL_NAME (field) == name) + return field; + + /* If *typep is an innerclass, lookup the field in its enclosing + contexts */ + if (INNER_CLASS_TYPE_P (*typep)) { - if (DECL_NAME (field) == name) + tree outer_type = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (*typep))); + + if ((field = lookup_field (&outer_type, name))) return field; } + + /* Process implemented interfaces. */ + basetype_vec = TYPE_BINFO_BASETYPES (*typep); + n = TREE_VEC_LENGTH (basetype_vec); + save_field = NULL_TREE; + for (i = 0; i < n; i++) + { + tree t = BINFO_TYPE (TREE_VEC_ELT (basetype_vec, i)); + 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 `%s' is ambiguous: appears in interface `%s' and interface `%s'", + 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; @@ -1140,8 +1484,8 @@ lookup_label (pc) int pc; { tree name; - char buf[20]; - sprintf (buf, "LJpc=%d", pc); + char buf[32]; + ASM_GENERATE_INTERNAL_LABEL(buf, "LJpc=", pc); name = get_identifier (buf); if (IDENTIFIER_LOCAL_VALUE (name)) return IDENTIFIER_LOCAL_VALUE (name); @@ -1162,8 +1506,9 @@ tree generate_name () { static int l_number = 0; - char buff [20]; - sprintf (buff, "$L%d", l_number++); + char buff [32]; + ASM_GENERATE_INTERNAL_LABEL(buff, "LJv", l_number); + l_number++; return get_identifier (buff); } @@ -1172,10 +1517,8 @@ create_label_decl (name) tree name; { tree decl; - push_obstacks (&permanent_obstack, &permanent_obstack); decl = build_decl (LABEL_DECL, name, TREE_TYPE (return_address_type_node)); - pop_obstacks (); DECL_CONTEXT (decl) = current_function_decl; DECL_IGNORED_P (decl) = 1; return decl; @@ -1184,9 +1527,9 @@ create_label_decl (name) /* This maps a bytecode offset (PC) to various flags. */ char *instruction_bits; -void +static void note_label (current_pc, target_pc) - int current_pc, target_pc; + int current_pc ATTRIBUTE_UNUSED, target_pc; { lookup_label (target_pc); instruction_bits [target_pc] |= BCODE_JUMP_TARGET; @@ -1195,7 +1538,7 @@ note_label (current_pc, target_pc) /* Emit code to jump to TARGET_PC if VALUE1 CONDITION VALUE2, where CONDITION is one of one the compare operators. */ -void +static void expand_compare (condition, value1, value2, target_pc) enum tree_code condition; tree value1, value2; @@ -1210,7 +1553,7 @@ expand_compare (condition, value1, value2, target_pc) /* Emit code for a TEST-type opcode. */ -void +static void expand_test (condition, type, target_pc) enum tree_code condition; tree type; @@ -1225,7 +1568,7 @@ expand_test (condition, type, target_pc) /* Emit code for a COND-type opcode. */ -void +static void expand_cond (condition, type, target_pc) enum tree_code condition; tree type; @@ -1240,7 +1583,7 @@ expand_cond (condition, type, target_pc) expand_compare (condition, value1, value2, target_pc); } -void +static void expand_java_goto (target_pc) int target_pc; { @@ -1249,7 +1592,8 @@ expand_java_goto (target_pc) expand_goto (target_label); } -void +#if 0 +static void expand_java_call (target_pc, return_address) int target_pc, return_address; { @@ -1260,9 +1604,9 @@ expand_java_call (target_pc, return_address) expand_goto (target_label); } -void +static void expand_java_ret (return_address) - tree return_address; + tree return_address ATTRIBUTE_UNUSED; { warning ("ret instruction not implemented"); #if 0 @@ -1271,40 +1615,23 @@ expand_java_ret (return_address) expand_goto (target_label); #endif } +#endif -/* Recursive helper function to pop argument types during verifiation. */ - -void -pop_argument_types (arg_types) - tree arg_types; -{ - if (arg_types == NULL_TREE) - return; - if (TREE_CODE (arg_types) == TREE_LIST) - { - pop_argument_types (TREE_CHAIN (arg_types)); - pop_type (TREE_VALUE (arg_types)); - return; - } - abort (); -} - -tree +static tree pop_arguments (arg_types) tree arg_types; { - if (arg_types == NULL_TREE) + if (arg_types == end_params_node) return NULL_TREE; if (TREE_CODE (arg_types) == TREE_LIST) { tree tail = pop_arguments (TREE_CHAIN (arg_types)); tree type = TREE_VALUE (arg_types); tree arg = pop_value (type); -#ifdef PROMOTE_PROTOTYPES - if (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) + if (PROMOTE_PROTOTYPES + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) && INTEGRAL_TYPE_P (type)) arg = convert (integer_type_node, arg); -#endif return tree_cons (NULL_TREE, arg, tail); } abort (); @@ -1318,14 +1645,48 @@ tree build_class_init (clas, expr) tree clas, expr; { - tree init; + tree init, call; + struct init_test_hash_entry *ite; if (inherits_from_p (current_class, clas)) return expr; - init = build (CALL_EXPR, void_type_node, - build_address_of (soft_initclass_node), - build_tree_list (NULL_TREE, build_class_ref (clas)), - NULL_TREE); - TREE_SIDE_EFFECTS (init) = 1; + + if (always_initialize_class_p) + { + init = build (CALL_EXPR, void_type_node, + build_address_of (soft_initclass_node), + build_tree_list (NULL_TREE, build_class_ref (clas)), + NULL_TREE); + TREE_SIDE_EFFECTS (init) = 1; + } + else + { + ite = (struct init_test_hash_entry *) + hash_lookup (&DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), + (const hash_table_key) clas, + TRUE, NULL); + + if (ite->init_test_decl == 0) + ite->init_test_decl = build_decl (VAR_DECL, NULL_TREE, + boolean_type_node); + /* Tell the check-init code to ignore this decl. */ + DECL_BIT_INDEX(ite->init_test_decl) = -1; + + init = build (CALL_EXPR, void_type_node, + build_address_of (soft_initclass_node), + build_tree_list (NULL_TREE, build_class_ref (clas)), + NULL_TREE); + TREE_SIDE_EFFECTS (init) = 1; + call = build (COMPOUND_EXPR, TREE_TYPE (expr), init, + build (MODIFY_EXPR, boolean_type_node, + ite->init_test_decl, boolean_true_node)); + TREE_SIDE_EFFECTS (call) = 1; + init = build (COND_EXPR, void_type_node, + build (EQ_EXPR, boolean_type_node, + ite->init_test_decl, boolean_false_node), + call, integer_zero_node); + TREE_SIDE_EFFECTS (init) = 1; + } + if (expr != NULL_TREE) { expr = build (COMPOUND_EXPR, TREE_TYPE (expr), init, expr); @@ -1335,18 +1696,15 @@ build_class_init (clas, expr) return init; } -static tree methods_ident = NULL_TREE; -static tree ncode_ident = NULL_TREE; -tree dtable_ident = NULL_TREE; - tree build_known_method_ref (method, method_type, self_type, method_signature, arg_list) - tree method, method_type, self_type, method_signature, arg_list; + tree method, method_type ATTRIBUTE_UNUSED, self_type, + method_signature ATTRIBUTE_UNUSED, arg_list ATTRIBUTE_UNUSED; { tree func; if (is_compiled_class (self_type)) { - make_decl_rtl (method, NULL, 1); + make_decl_rtl (method, NULL); func = build1 (ADDR_EXPR, method_ptr_type_node, method); } else @@ -1408,7 +1766,7 @@ invoke_build_dtable (is_invoke_interface, arg_list) object_type_node : TREE_VALUE (arg_list)); if (dtable_ident == NULL_TREE) - dtable_ident = get_identifier ("dtable"); + dtable_ident = get_identifier ("vtable"); dtable = build1 (INDIRECT_REF, object_type_node, objectref ); dtable = build (COMPONENT_REF, dtable_ptr_type, dtable, lookup_field (&object_type_node, dtable_ident)); @@ -1423,40 +1781,64 @@ build_invokevirtual (dtable, method) tree func; tree nativecode_ptr_ptr_type_node = build_pointer_type (nativecode_ptr_type_node); - int method_index = TREE_INT_CST_LOW (DECL_VINDEX (method)); + tree method_index = convert (sizetype, DECL_VINDEX (method)); + /* Add one to skip "class" field of dtable, and one to skip unused vtable entry (for C++ compatibility). */ - method_index += 2; - method_index - *= int_size_in_bytes (nativecode_ptr_ptr_type_node); - func = fold (build (PLUS_EXPR, nativecode_ptr_ptr_type_node, - dtable, build_int_2 (method_index, 0))); + method_index = size_binop (PLUS_EXPR, method_index, size_int (2)); + method_index = size_binop (MULT_EXPR, method_index, + TYPE_SIZE_UNIT (nativecode_ptr_ptr_type_node)); + func = fold (build (PLUS_EXPR, nativecode_ptr_ptr_type_node, dtable, + convert (nativecode_ptr_ptr_type_node, method_index))); func = build1 (INDIRECT_REF, nativecode_ptr_type_node, func); return func; } tree -build_invokeinterface (dtable, method_name, method_signature) - tree dtable, method_name, method_signature; +build_invokeinterface (dtable, method) + tree dtable, method; { static tree class_ident = NULL_TREE; tree lookup_arg; + tree interface; + tree idx; + tree meth; + int i; /* We expand invokeinterface here. _Jv_LookupInterfaceMethod() will ensure that the selected method exists, is public and not abstract nor static. */ if (class_ident == NULL_TREE) - class_ident = get_identifier ("class"); - + { + class_ident = get_identifier ("class"); + ggc_add_tree_root (&class_ident, 1); + } + dtable = build1 (INDIRECT_REF, dtable_type, dtable); dtable = build (COMPONENT_REF, class_ptr_type, dtable, lookup_field (&dtable_type, class_ident)); - lookup_arg = build_tree_list (NULL_TREE, build_utf8_ref (method_signature)); + + interface = DECL_CONTEXT (method); + layout_class_methods (interface); + + i = 1; + for (meth = TYPE_METHODS (interface); ; meth = TREE_CHAIN (meth), i++) + { + if (meth == method) + { + idx = build_int_2 (i, 0); + break; + } + if (meth == NULL_TREE) + fatal ("internal error in build_invokeinterface"); + } + lookup_arg = tree_cons (NULL_TREE, dtable, - tree_cons (NULL_TREE, build_utf8_ref (method_name), - lookup_arg)); + tree_cons (NULL_TREE, build_class_ref (interface), + build_tree_list (NULL_TREE, idx))); + return build (CALL_EXPR, ptr_type_node, build_address_of (soft_lookupinterfacemethod_node), lookup_arg, NULL_TREE); @@ -1467,28 +1849,30 @@ build_invokeinterface (dtable, method_name, method_signature) METHOD_REF_INDEX is an index into the constant pool. NARGS is the number of arguments, or -1 if not specified. */ -void +static void expand_invoke (opcode, method_ref_index, nargs) int opcode; int method_ref_index; - int nargs; + 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)); - char *self_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type))); + const char *self_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type))); tree call, func, method, arg_list, method_type; + tree cond = 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 ("failed to find class '%s'", self_name); } layout_class_methods (self_type); - if (method_name == init_identifier_node) + if (ID_INIT_P (method_name)) method = lookup_java_constructor (CLASS_TO_HANDLE_TYPE (self_type), method_signature); else @@ -1540,11 +1924,29 @@ expand_invoke (opcode, method_ref_index, nargs) flush_quick_stack (); func = NULL_TREE; - if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial - || (opcode == OPCODE_invokevirtual - && (METHOD_FINAL (method) || CLASS_FINAL (TYPE_NAME (self_type))))) + if (opcode == OPCODE_invokestatic) func = build_known_method_ref (method, method_type, self_type, method_signature, arg_list); + 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 use a SAVE_EXPR here to make sure we only evaluate + the new `self' expression once. */ + tree save_arg = save_expr (TREE_VALUE (arg_list)); + TREE_VALUE (arg_list) = save_arg; + cond = build (EQ_EXPR, boolean_type_node, save_arg, null_pointer_node); + func = build_known_method_ref (method, method_type, self_type, + method_signature, arg_list); + } else { tree dtable = invoke_build_dtable (opcode == OPCODE_invokeinterface, @@ -1552,12 +1954,29 @@ expand_invoke (opcode, method_ref_index, nargs) if (opcode == OPCODE_invokevirtual) func = build_invokevirtual (dtable, method); else - func = build_invokeinterface (dtable, method_name, method_signature); + func = build_invokeinterface (dtable, method); } func = build1 (NOP_EXPR, build_pointer_type (method_type), func); call = build (CALL_EXPR, TREE_TYPE (method_type), func, arg_list, NULL_TREE); TREE_SIDE_EFFECTS (call) = 1; + if (cond != NULL_TREE) + { + /* We have to make the `then' branch a compound expression to + make the types turn out right. This seems bizarre. */ + call = build (COND_EXPR, TREE_TYPE (call), cond, + build (COMPOUND_EXPR, TREE_TYPE (call), + build (CALL_EXPR, void_type_node, + build_address_of (soft_nullpointer_node), + NULL_TREE, NULL_TREE), + (FLOAT_TYPE_P (TREE_TYPE (call)) + ? build_real (TREE_TYPE (call), dconst0) + : build1 (CONVERT_EXPR, TREE_TYPE (call), + integer_zero_node))), + call); + TREE_SIDE_EFFECTS (call) = 1; + } + if (TREE_CODE (TREE_TYPE (method_type)) == VOID_TYPE) expand_expr_stmt (call); else @@ -1567,23 +1986,199 @@ expand_invoke (opcode, method_ref_index, nargs) } } +/* Create a stub which will be put into the vtable but which will call + a JNI function. */ + +tree +build_jni_stub (method) + tree method; +{ + tree jnifunc, call, args, body, lookup_arg, method_sig, arg_types; + tree jni_func_type, tem; + tree env_var, res_var = NULL_TREE, block; + tree method_args, res_type; + tree meth_var; + + tree klass = DECL_CONTEXT (method); + int from_class = ! CLASS_FROM_SOURCE_P (klass); + klass = build_class_ref (klass); + + if (! METHOD_NATIVE (method) || ! flag_jni) + abort (); + + DECL_ARTIFICIAL (method) = 1; + DECL_EXTERNAL (method) = 0; + + env_var = build_decl (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"), + 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; + make_decl_rtl (meth_var, NULL); + meth_var = pushdecl_top_level (meth_var); + + /* 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)); + block = build_block (env_var, NULL_TREE, 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)); + + /* Compute the local `env' by calling _Jv_GetJNIEnvNewFrame. */ + body = build (MODIFY_EXPR, ptr_type_node, env_var, + build (CALL_EXPR, ptr_type_node, + build_address_of (soft_getjnienvnewframe_node), + build_tree_list (NULL_TREE, klass), + NULL_TREE)); + CAN_COMPLETE_NORMALLY (body) = 1; + + /* 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. */ + args = NULL_TREE; + for (tem = method_args; tem != NULL_TREE; tem = TREE_CHAIN (tem)) + args = tree_cons (NULL_TREE, tem, args); + args = nreverse (args); + arg_types = TYPE_ARG_TYPES (TREE_TYPE (method)); + + /* 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 = tree_cons (NULL_TREE, klass, args); + arg_types = tree_cons (NULL_TREE, object_ptr_type_node, arg_types); + } + + /* The JNIEnv structure is the first argument to the JNI function. */ + args = tree_cons (NULL_TREE, env_var, args); + 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)); + lookup_arg = + build_tree_list (NULL_TREE, + build_utf8_ref (unmangle_classname + (IDENTIFIER_POINTER (method_sig), + IDENTIFIER_LENGTH (method_sig)))); + tem = DECL_NAME (method); + lookup_arg + = tree_cons (NULL_TREE, klass, + tree_cons (NULL_TREE, build_utf8_ref (tem), lookup_arg)); + + jni_func_type + = build_pointer_type (build_function_type (TREE_TYPE (TREE_TYPE (method)), + arg_types)); + + jnifunc = build (COND_EXPR, ptr_type_node, + meth_var, meth_var, + build (MODIFY_EXPR, ptr_type_node, + meth_var, + build (CALL_EXPR, ptr_type_node, + build_address_of (soft_lookupjnimethod_node), + lookup_arg, NULL_TREE))); + + /* Now we make the actual JNI call via the resulting function + pointer. */ + call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (method)), + build1 (NOP_EXPR, jni_func_type, jnifunc), + args, NULL_TREE); + + /* 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) + call = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (method)), + res_var, call); + + TREE_SIDE_EFFECTS (call) = 1; + CAN_COMPLETE_NORMALLY (call) = 1; + + body = build (COMPOUND_EXPR, void_type_node, body, call); + TREE_SIDE_EFFECTS (body) = 1; + + /* Now free the environment we allocated. */ + call = build (CALL_EXPR, ptr_type_node, + build_address_of (soft_jnipopsystemframe_node), + build_tree_list (NULL_TREE, env_var), + NULL_TREE); + TREE_SIDE_EFFECTS (call) = 1; + CAN_COMPLETE_NORMALLY (call) = 1; + body = build (COMPOUND_EXPR, void_type_node, body, call); + TREE_SIDE_EFFECTS (body) = 1; + + /* Finally, do the return. When compiling from source we rely on + patch_return to patch the return value -- because DECL_RESULT is + not set at the time this function is called. */ + if (from_class) + { + res_type = void_type_node; + if (res_var != NULL_TREE) + { + tree drt; + if (! DECL_RESULT (method)) + abort (); + /* 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 = build (MODIFY_EXPR, drt, DECL_RESULT (method), res_var); + TREE_SIDE_EFFECTS (res_var) = 1; + } + } + else + { + /* This is necessary to get patch_return to run. */ + res_type = NULL_TREE; + } + body = build (COMPOUND_EXPR, void_type_node, body, + build1 (RETURN_EXPR, res_type, res_var)); + TREE_SIDE_EFFECTS (body) = 1; + + BLOCK_EXPR_BODY (block) = body; + return block; +} /* 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. */ -void +static void expand_java_field_op (is_static, is_putting, field_ref_index) 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)); - char *self_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type))); + 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_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; @@ -1619,7 +2214,7 @@ expand_java_field_op (is_static, is_putting, field_ref_index) this is also needed to avoid circularities in the implementation of these fields in libjava. */ if (field_name == TYPE_identifier_node && ! is_putting - && field_type == class_ptr_type + && ! flag_emit_class_files && field_type == class_ptr_type && strncmp (self_name, "java.lang.", 10) == 0) { tree typ = build_primtype_type_ref (self_name); @@ -1643,15 +2238,16 @@ expand_java_field_op (is_static, is_putting, field_ref_index) "assignment to final field `%s' not in field's class"); else if (FIELD_STATIC (field_decl)) { - if (DECL_NAME (current_function_decl) != clinit_identifier_node) - error_with_decl (field_decl, + if (!DECL_CLINIT_P (current_function_decl)) + warning_with_decl (field_decl, "assignment to final static field `%s' not in class initializer"); } else { - if (! DECL_CONSTRUCTOR_P (current_function_decl)) - error_with_decl (field_decl, - "assignment to final field `%s' not in constructor"); + tree cfndecl_name = DECL_NAME (current_function_decl); + if (! DECL_CONSTRUCTOR_P (current_function_decl) + && !ID_FINIT_P (cfndecl_name)) + warning_with_decl (field_decl, "assignment to final field `%s' not in constructor"); } } expand_assignment (field_ref, new_value, 0, 0); @@ -1662,9 +2258,9 @@ expand_java_field_op (is_static, is_putting, field_ref_index) tree build_primtype_type_ref (self_name) - char *self_name; + const char *self_name; { - char *class_name = self_name+10; + const char *class_name = self_name+10; tree typ; if (strncmp(class_name, "Byte", 4) == 0) typ = byte_type_node; @@ -1716,28 +2312,118 @@ case_identity (t, v) return v; } +/* Return the name of the vtable for an array of a given primitive + type. */ +static tree +get_primitive_array_vtable (tree elt) +{ + tree r; + if (elt == boolean_type_node) + r = boolean_array_vtable; + else if (elt == byte_type_node) + r = byte_array_vtable; + else if (elt == char_type_node) + r = char_array_vtable; + else if (elt == short_type_node) + r = short_array_vtable; + else if (elt == int_type_node) + r = int_array_vtable; + else if (elt == long_type_node) + r = long_array_vtable; + else if (elt == float_type_node) + r = float_array_vtable; + else if (elt == double_type_node) + r = double_array_vtable; + else + abort (); + return build_address_of (r); +} + struct rtx_def * java_lang_expand_expr (exp, target, tmode, modifier) register tree exp; - rtx target; - enum machine_mode tmode; - enum expand_modifier modifier; + rtx target ATTRIBUTE_UNUSED; + enum machine_mode tmode ATTRIBUTE_UNUSED; + enum expand_modifier modifier ATTRIBUTE_UNUSED; { - register rtx op0; - tree type = TREE_TYPE (exp); - register enum machine_mode mode = TYPE_MODE (type); - int unsignedp = TREE_UNSIGNED (type); - tree node, current; - int has_finally_p; + tree current; switch (TREE_CODE (exp)) { + case NEW_ARRAY_INIT: + { + rtx tmp; + tree array_type = TREE_TYPE (TREE_TYPE (exp)); + tree element_type = TYPE_ARRAY_ELEMENT (array_type); + tree data_fld = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (array_type))); + HOST_WIDE_INT ilength = java_array_type_length (array_type); + tree length = build_int_2 (ilength, 0); + tree init = TREE_OPERAND (exp, 0); + tree array_decl; + + /* See if we can generate the array statically. */ + if (TREE_CONSTANT (init) && TREE_STATIC (exp) + && JPRIMITIVE_TYPE_P (element_type)) + { + tree temp, value, init_decl; + struct rtx_def *r; + START_RECORD_CONSTRUCTOR (temp, object_type_node); + PUSH_FIELD_VALUE (temp, "vtable", + get_primitive_array_vtable (element_type)); + if (! flag_hash_synchronization) + PUSH_FIELD_VALUE (temp, "sync_info", null_pointer_node); + FINISH_RECORD_CONSTRUCTOR (temp); + START_RECORD_CONSTRUCTOR (value, array_type); + PUSH_SUPER_VALUE (value, temp); + /* FIXME: build a new `length' here to get it on the right + obstack. */ + PUSH_FIELD_VALUE (value, "length", build_int_2 (ilength, 0)); + PUSH_FIELD_VALUE (value, "data", init); + FINISH_RECORD_CONSTRUCTOR (value); + + init_decl = build_decl (VAR_DECL, generate_name (), array_type); + pushdecl_top_level (init_decl); + TREE_STATIC (init_decl) = 1; + DECL_INITIAL (init_decl) = value; + DECL_IGNORED_P (init_decl) = 1; + TREE_READONLY (init_decl) = 1; + TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (init_decl)) = 1; + make_decl_rtl (init_decl, NULL); + init = build1 (ADDR_EXPR, TREE_TYPE (exp), init_decl); + r = expand_expr (init, target, tmode, modifier); + return r; + } + + array_decl = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp)); + expand_decl (array_decl); + tmp = expand_assignment (array_decl, + build_new_array (element_type, length), + 1, 0); + if (TREE_CONSTANT (init) + && ilength >= 10 && JPRIMITIVE_TYPE_P (element_type)) + { + tree init_decl; + init_decl = build_decl (VAR_DECL, generate_name (), + TREE_TYPE (init)); + pushdecl_top_level (init_decl); + TREE_STATIC (init_decl) = 1; + DECL_INITIAL (init_decl) = init; + DECL_IGNORED_P (init_decl) = 1; + TREE_READONLY (init_decl) = 1; + TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (init_decl)) = 1; + make_decl_rtl (init_decl, NULL); + init = init_decl; + } + expand_assignment (build (COMPONENT_REF, TREE_TYPE (data_fld), + build1 (INDIRECT_REF, array_type, + array_decl), data_fld), init, 0, 0); + return tmp; + } case BLOCK: if (BLOCK_EXPR_BODY (exp)) { tree local; tree body = BLOCK_EXPR_BODY (exp); - struct rtx_def *to_return; pushlevel (2); /* 2 and above */ expand_start_bindings (0); local = BLOCK_EXPR_DECLS (exp); @@ -1755,12 +2441,13 @@ java_lang_expand_expr (exp, target, tmode, modifier) emit_queue (); body = TREE_OPERAND (body, 1); } - to_return = expand_expr (body, target, tmode, modifier); + expand_expr (body, const0_rtx, VOIDmode, 0); + emit_queue (); poplevel (1, 1, 0); expand_end_bindings (getdecls (), 1, 0); - return to_return; + return const0_rtx; } - break; + return const0_rtx; case CASE_EXPR: { @@ -1789,56 +2476,29 @@ java_lang_expand_expr (exp, target, tmode, modifier) return const0_rtx; case TRY_EXPR: - /* We expand a try[-catch][-finally] block */ + /* We expand a try[-catch] block */ /* Expand the try block */ expand_eh_region_start (); expand_expr_stmt (TREE_OPERAND (exp, 0)); expand_start_all_catch (); - has_finally_p = (TREE_OPERAND (exp, 2) ? 1 : 0); /* Expand all catch clauses (EH handlers) */ for (current = TREE_OPERAND (exp, 1); current; current = TREE_CHAIN (current)) { - extern rtx return_label; tree type; - /* If we have a finally, the last exception handler is the - one that is supposed to catch everything. */ - if (has_finally_p && !TREE_CHAIN (current)) - type = NULL_TREE; - else - { - tree catch = java_get_catch_block (current, has_finally_p); - tree decl = BLOCK_EXPR_DECLS (catch); - type = (decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE); - } + tree catch = TREE_OPERAND (current, 0); + tree decl = BLOCK_EXPR_DECLS (catch); + type = (decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE); start_catch_handler (prepare_eh_table_type (type)); expand_expr_stmt (TREE_OPERAND (current, 0)); - /* Need to expand a goto to the end of the function here, - but not for the catch everything handler. */ - if (type) - { - if (return_label) - emit_jump (return_label); - else - fatal ("No return_label for this function - " - "java_lang_expand_expr"); - } + expand_resume_after_catch (); end_catch_handler (); } - - /* Expand the finally block, if any */ - if (has_finally_p) - { - tree finally = TREE_OPERAND (exp, 2); - if (FINALLY_EXPR_LABEL (finally)) - emit_label (label_rtx (FINALLY_EXPR_LABEL (finally))); - expand_expr_stmt (FINALLY_EXPR_BLOCK (finally)); - } expand_end_all_catch (); - break; + return const0_rtx; default: fatal ("Can't expand '%s' tree - java_lang_expand_expr", @@ -1846,17 +2506,20 @@ java_lang_expand_expr (exp, target, tmode, modifier) } } +/* Go over METHOD's bytecode and note instruction starts in + instruction_bits[]. */ + void -expand_byte_code (jcf, method) +note_instructions (jcf, method) JCF *jcf; tree method; { - int PC; - int i; + int PC; + unsigned char* byte_ops; + long length = DECL_CODE_LENGTH (method); + int saw_index; - unsigned char *linenumber_pointer; - struct eh_range *prev_eh_ranges = NULL_EH_RANGE; - struct eh_range *eh_ranges; + jint INT_temp; #undef RET /* Defined by config/i386/i386.h */ #undef AND /* Causes problems with opcodes for iand and land. */ @@ -1871,43 +2534,19 @@ expand_byte_code (jcf, method) #define FLOAT_type_node float_type_node #define DOUBLE_type_node double_type_node #define VOID_type_node void_type_node - jint INT_temp; - unsigned char* byte_ops; - long length = DECL_CODE_LENGTH (method); - - stack_pointer = 0; - JCF_SEEK (jcf, DECL_CODE_OFFSET (method)); - byte_ops = jcf->read_ptr; - #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) 1 /* Already handled by verifier. */ - - instruction_bits = oballoc (length + 1); - bzero (instruction_bits, length + 1); +#define CHECK_PC_IN_RANGE(PC) ((void)1) /* Already handled by verifier. */ - /* 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 ("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; - } - } + JCF_SEEK (jcf, DECL_CODE_OFFSET (method)); + byte_ops = jcf->read_ptr; + instruction_bits = xrealloc (instruction_bits, length + 1); + memset (instruction_bits, 0, length + 1); - /* Do a preliminary pass. - * This figures out which PC can be the targets of jumps. */ + /* This pass figures out which PC can be the targets of jumps. */ for (PC = 0; PC < length;) { int oldpc = PC; /* PC at instruction start. */ @@ -1954,8 +2593,6 @@ expand_byte_code (jcf, method) } \ } -/* nothing */ /* XXX JH */ - #define PRE_IMPL(IGNORE1, IGNORE2) /* nothing */ #define PRE_MONITOR(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ @@ -1990,7 +2627,8 @@ expand_byte_code (jcf, method) NOTE_LABEL (default_offset+oldpc); \ if (npairs >= 0) \ while (--npairs >= 0) { \ - jint match = IMMEDIATE_s4; jint offset = IMMEDIATE_s4; \ + jint match ATTRIBUTE_UNUSED = IMMEDIATE_s4; \ + jint offset = IMMEDIATE_s4; \ NOTE_LABEL (offset+oldpc); } \ } @@ -2014,6 +2652,40 @@ expand_byte_code (jcf, method) #undef JAVAOP } } /* for */ +} + +void +expand_byte_code (jcf, method) + 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); + + 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 ("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 (jcf, byte_ops, length)) return; @@ -2034,15 +2706,29 @@ expand_byte_code (jcf, method) if (! (instruction_bits [PC] & BCODE_VERIFIED)) { - /* never executed - skip */ - warning ("Some bytecode operations (starting at pc %d) can never be executed", PC); - while (PC < length - && ! (instruction_bits [PC] & BCODE_VERIFIED)) - PC++; - continue; + 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. */ + warning ("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, @@ -2066,17 +2752,20 @@ expand_byte_code (jcf, method) } } } - maybe_start_try (PC); maybe_pushlevels (PC); - PC = process_jvm_instruction (PC, byte_ops, length); - maybe_poplevels (PC); - maybe_end_try (PC); } /* for */ + + if (dead_code_index != -1) + { + /* We've just reached the end of a region of dead code. */ + warning ("Unreachable bytecode from %d to the end of the method.", + dead_code_index); + } } -void +static void java_push_constant_from_pool (jcf, index) JCF *jcf; int index; @@ -2085,12 +2774,10 @@ java_push_constant_from_pool (jcf, index) if (JPOOL_TAG (jcf, index) == CONSTANT_String) { tree name; - push_obstacks (&permanent_obstack, &permanent_obstack); name = get_name_constant (jcf, JPOOL_USHORT1 (jcf, index)); index = alloc_name_constant (CONSTANT_String, name); c = build_ref_from_constant_pool (index); TREE_TYPE (c) = promote_type (string_type_node); - pop_obstacks (); } else c = get_constant (jcf, index); @@ -2100,18 +2787,18 @@ java_push_constant_from_pool (jcf, index) int process_jvm_instruction (PC, byte_ops, length) int PC; - unsigned char* byte_ops; - long length; + const unsigned char* byte_ops; + long length ATTRIBUTE_UNUSED; { - char *opname; /* Temporary ??? */ + const char *opname; /* Temporary ??? */ int oldpc = PC; /* PC at instruction start. */ /* If the instruction is at the beginning of a exception handler, replace the top of the stack with the thrown object reference */ if (instruction_bits [PC] & BCODE_EXCEPTION_TARGET) { - pop_value (ptr_type_node); - push_value (soft_exceptioninfo_call_node); + tree type = pop_type (ptr_type_node); + push_value (build1 (NOP_EXPR, type, soft_exceptioninfo_call_node)); } switch (byte_ops[PC++]) @@ -2134,6 +2821,7 @@ process_jvm_instruction (PC, byte_ops, length) tree where = lookup_label (oldpc+OPERAND_VALUE); \ tree ret = lookup_label (PC); \ build_java_jsr (where, ret); \ + load_type_state (ret); \ } /* Push a constant onto the stack. */ @@ -2217,7 +2905,6 @@ process_jvm_instruction (PC, byte_ops, length) tree type = TREE_TYPE (selector); \ flush_quick_stack (); \ expand_start_case (0, selector, type, "switch statement");\ - push_momentary (); \ while (--npairs >= 0) \ { \ jint match = IMMEDIATE_s4; jint offset = IMMEDIATE_s4; \ @@ -2230,7 +2917,6 @@ process_jvm_instruction (PC, byte_ops, length) label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); \ pushcase (NULL_TREE, 0, label, &duplicate); \ expand_java_goto (oldpc + default_offset); \ - pop_momentary (); \ expand_end_case (selector); \ } @@ -2242,7 +2928,6 @@ process_jvm_instruction (PC, byte_ops, length) tree type = TREE_TYPE (selector); \ flush_quick_stack (); \ expand_start_case (0, selector, type, "switch statement");\ - push_momentary (); \ for (; low <= high; low++) \ { \ jint offset = IMMEDIATE_s4; \ @@ -2255,7 +2940,6 @@ process_jvm_instruction (PC, byte_ops, length) label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); \ pushcase (NULL_TREE, 0, label, &duplicate); \ expand_java_goto (oldpc + default_offset); \ - pop_momentary (); \ expand_end_case (selector); \ } @@ -2412,3 +3096,191 @@ process_jvm_instruction (PC, byte_ops, length) } return PC; } + +/* Return the opcode at PC in the code section pointed to by + CODE_OFFSET. */ + +static unsigned char +peek_opcode_at_pc (jcf, code_offset, pc) + JCF *jcf; + int code_offset, 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 store_ + PC+1 ... + + Attribute "LocalVariableTable" + slot #: ... (PC: PC+1 length: L) + + This is accurate because the local in slot 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 a 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 (jcf, code_offset, start_pc, slot) + struct JCF *jcf; + int code_offset, start_pc, 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 store_, index >0 + means we have a 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. +*/ + +tree +force_evaluation_order (node) + tree node; +{ + if (flag_syntax_only) + return node; + if (TREE_CODE_CLASS (TREE_CODE (node)) == '2') + { + if (TREE_SIDE_EFFECTS (TREE_OPERAND (node, 1))) + TREE_OPERAND (node, 0) = save_expr (TREE_OPERAND (node, 0)); + } + else if (TREE_CODE (node) == CALL_EXPR || TREE_CODE (node) == NEW_CLASS_EXPR) + { + tree arg, cmp; + + if (!TREE_OPERAND (node, 1)) + return node; + + /* This reverses the evaluation order. This is a desired effect. */ + for (cmp = NULL_TREE, arg = TREE_OPERAND (node, 1); + arg; arg = TREE_CHAIN (arg)) + { + tree saved = save_expr (force_evaluation_order (TREE_VALUE (arg))); + cmp = (cmp == NULL_TREE ? saved : + build (COMPOUND_EXPR, void_type_node, cmp, saved)); + TREE_VALUE (arg) = saved; + } + + if (cmp && TREE_CODE (cmp) == COMPOUND_EXPR) + TREE_SIDE_EFFECTS (cmp) = 1; + + if (cmp) + { + cmp = save_expr (build (COMPOUND_EXPR, TREE_TYPE (node), cmp, node)); + CAN_COMPLETE_NORMALLY (cmp) = CAN_COMPLETE_NORMALLY (node); + TREE_SIDE_EFFECTS (cmp) = 1; + node = cmp; + } + } + return node; +}