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