aboutsummaryrefslogtreecommitdiff
path: root/gcc/java/verify.c
diff options
context:
space:
mode:
authorAnthony Green <green@gcc.gnu.org>1998-09-06 15:36:06 +0000
committerAnthony Green <green@gcc.gnu.org>1998-09-06 15:36:06 +0000
commite04a16fbeee9504247e0d069171a627096225028 (patch)
treeb78f906318225a5e7bd3471f008be772727bdcea /gcc/java/verify.c
parentfc08e32958a1df77493bfe4abc52f88c7ade6251 (diff)
downloadgcc-e04a16fbeee9504247e0d069171a627096225028.zip
gcc-e04a16fbeee9504247e0d069171a627096225028.tar.gz
gcc-e04a16fbeee9504247e0d069171a627096225028.tar.bz2
Initial revision
From-SVN: r22299
Diffstat (limited to 'gcc/java/verify.c')
-rw-r--r--gcc/java/verify.c1239
1 files changed, 1239 insertions, 0 deletions
diff --git a/gcc/java/verify.c b/gcc/java/verify.c
new file mode 100644
index 0000000..82a081f
--- /dev/null
+++ b/gcc/java/verify.c
@@ -0,0 +1,1239 @@
+/* Handle verification of bytecoded methods for the GNU compiler for
+ the Java(TM) language.
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+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. */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "java-tree.h"
+#include "javaop.h"
+#include "java-opcodes.h"
+#include "jcf.h"
+#include "java-except.h"
+
+extern int stack_pointer;
+
+/* During verification, start of the current subroutine (jsr target). */
+tree current_subr;
+
+/* A list of pending blocks, chained using LABEL_PENDING_CHAIN.
+ A pending block is one that has LABEL_CHANGED set, which means
+ it requires (re-) verification. */
+tree pending_blocks;
+
+/* Append TARGET_LABEL to the pending_block stack unless already in it. */
+
+void
+push_pending_label (target_label)
+ tree target_label;
+{
+ if (! LABEL_CHANGED (target_label))
+ {
+ LABEL_PENDING_CHAIN (target_label) = pending_blocks;
+ pending_blocks = target_label;
+ LABEL_CHANGED (target_label) = 1;
+ }
+}
+
+/* Note that TARGET_LABEL is a possible successor instruction.
+ Merge the type state etc.
+ Return NULL on sucess, or an error message on failure. */
+
+static char *
+check_pending_block (target_label)
+ tree target_label;
+{
+ int changed = merge_type_state (target_label);
+
+ if (changed)
+ {
+ if (changed < 0)
+ return "types could not be merged";
+ push_pending_label (target_label);
+ }
+
+ if (current_subr == NULL)
+ {
+ if (LABEL_IN_SUBR (target_label))
+ return "might transfer control into subroutine";
+ }
+ else
+ {
+ if (LABEL_IN_SUBR (target_label))
+ {
+ if (LABEL_SUBR_START (target_label) != current_subr)
+ return "transfer out of subroutine";
+ }
+ else if (! LABEL_VERIFIED (target_label))
+ {
+ LABEL_IN_SUBR (target_label) = 1;
+ LABEL_SUBR_START (target_label) = current_subr;
+ }
+ else
+ return "transfer out of subroutine";
+ }
+ return NULL;
+}
+
+/* Return the "merged" types of TYPE1 and TYPE2.
+ If either is primitive, the other must match (after promotion to int).
+ For reference types, return the common super-class.
+ Return TYPE_UNKNOWN if the types cannot be merged. */
+
+tree
+merge_types (type1, type2)
+ tree type1, type2;
+{
+ if (type1 == type2)
+ return type1;
+ if (type1 == TYPE_UNKNOWN || type2 == TYPE_UNKNOWN
+ || type1 == TYPE_RETURN_ADDR || type2 == TYPE_RETURN_ADDR)
+ return TYPE_UNKNOWN;
+ if (TREE_CODE (type1) == POINTER_TYPE && TREE_CODE (type2) == POINTER_TYPE)
+ {
+ int depth1, depth2;
+ tree tt1, tt2;
+ /* ptr_type_node is only used for a null reference,
+ which is compatible with any reference type. */
+ if (type1 == ptr_type_node || type2 == object_ptr_type_node)
+ return type2;
+ if (type2 == ptr_type_node || type1 == object_ptr_type_node)
+ return type1;
+
+ tt1 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type1));
+ tt2 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type2));
+
+ if (TYPE_ARRAY_P (tt1) || TYPE_ARRAY_P (tt2))
+ {
+ if (TYPE_ARRAY_P (tt1) == TYPE_ARRAY_P (tt2))
+ {
+ tree el_type1 = TYPE_ARRAY_ELEMENT (tt1);
+ tree el_type2 = TYPE_ARRAY_ELEMENT (tt2);
+ tree el_type = NULL_TREE;
+ if (el_type1 == el_type2)
+ el_type = el_type1;
+ else if (TREE_CODE (el_type1) == POINTER_TYPE
+ && TREE_CODE (el_type2) == POINTER_TYPE)
+ el_type = merge_types (el_type1, el_type2);
+ if (el_type != NULL_TREE)
+ {
+ HOST_WIDE_INT len1 = java_array_type_length (tt1);
+ HOST_WIDE_INT len2 = java_array_type_length (tt2);
+ if (len1 != len2)
+ len1 = -1;
+ else if (el_type1 == el_type2)
+ return type1;
+ return promote_type (build_java_array_type (el_type, len1));
+ }
+ }
+ return object_ptr_type_node;
+ }
+ type1 = tt1;
+ type2 = tt2;
+
+ depth1 = class_depth (type1);
+ depth2 = class_depth (type2);
+ for ( ; depth1 > depth2; depth1--)
+ type1 = TYPE_BINFO_BASETYPE (type1, 0);
+ for ( ; depth2 > depth1; depth2--)
+ type2 = TYPE_BINFO_BASETYPE (type2, 0);
+ while (type1 != type2)
+ {
+ type1 = TYPE_BINFO_BASETYPE (type1, 0);
+ type2 = TYPE_BINFO_BASETYPE (type2, 0);
+ }
+ return promote_type (type1);
+ }
+ if (INTEGRAL_TYPE_P (type1) && INTEGRAL_TYPE_P (type2)
+ && TYPE_PRECISION (type1) <= 32 && TYPE_PRECISION (type2) <= 32)
+ return int_type_node;
+ return TYPE_UNKNOWN;
+}
+
+/* Merge the current type state with that at LABEL.
+ Return -1 the the states are incompatible (i.e. on error),
+ 0 if there was no change, and 1 if there was a change. */
+
+int
+merge_type_state (label)
+ tree label;
+{
+ int nlocals = DECL_MAX_LOCALS(current_function_decl);
+ int cur_length = stack_pointer + nlocals;
+ tree vec = LABEL_TYPE_STATE (label);
+ tree return_map;
+ if (vec == NULL_TREE)
+ {
+ vec = make_tree_vec (cur_length);
+ LABEL_TYPE_STATE (label) = vec;
+ while (--cur_length >= 0)
+ TREE_VEC_ELT (vec, cur_length) = type_map [cur_length];
+ return 1;
+ }
+ else
+ {
+ int i;
+ int changed = 0;
+ if (LABEL_IS_SUBR_START (label) && LABEL_VERIFIED (label)
+ && current_subr != label)
+ return_map = LABEL_RETURN_TYPE_STATE (label);
+ else
+ return_map = NULL_TREE;
+ if (TREE_VEC_LENGTH (vec) != cur_length)
+ {
+ return -1;
+ }
+ for (i = 0; i < cur_length; i++)
+ {
+ tree old_type = TREE_VEC_ELT (vec, i);
+ tree new_type = merge_types (old_type, type_map [i]);
+ if (TREE_VEC_ELT (vec, i) != new_type)
+ {
+ /* If there has been a change, note that since we must re-verify.
+ However, if the label is the start of a subroutine,
+ we don't care about local variables that are neither
+ set nor used in the sub-routine. */
+ if (return_map == NULL_TREE || i >= nlocals
+ || TREE_VEC_ELT (return_map, i) != TYPE_UNUSED
+ || (TYPE_IS_WIDE (new_type)
+ && TREE_VEC_ELT (return_map, i+1) != TYPE_UNUSED))
+ changed = 1;
+ }
+ TREE_VEC_ELT (vec, i) = new_type;
+ if (new_type == TYPE_UNKNOWN)
+ {
+ if (i >= nlocals)
+ return -1;
+ }
+ else if (TYPE_IS_WIDE (new_type))
+ i++;
+ }
+ return changed;
+ }
+}
+
+/* Handle dup-like operations. */
+
+static void
+type_stack_dup (size, offset)
+ int size, offset;
+{
+ tree type[4];
+ int index;
+ if (size + offset > stack_pointer)
+ error ("stack underflow - dup* operation");
+ for (index = 0; index < size + offset; index++)
+ {
+ type[index] = stack_type_map[stack_pointer - 1];
+ if (type[index] == void_type_node)
+ {
+ index++;
+ type[index] = stack_type_map[stack_pointer - 2];
+ if (! TYPE_IS_WIDE (type[index]))
+ fatal ("internal error - dup operation");
+ if (index == size || index == size + offset)
+ fatal ("dup operation splits 64-bit number");
+ }
+ pop_type (type[index]);
+ }
+ for (index = size; --index >= 0; )
+ {
+ if (type[index] != void_type_node)
+ push_type (type[index]);
+ }
+
+ for (index = size + offset; --index >= 0; )
+ {
+ if (type[index] != void_type_node)
+ push_type (type[index]);
+ }
+}
+
+/* This causes the next iteration to ignore the next instruction
+ and look for some other unhandled instruction. */
+#define INVALIDATE_PC (prevpc = -1, oldpc = PC, PC = INVALID_PC)
+#define INVALID_PC (-1)
+
+#define VERIFICATION_ERROR(MESSAGE) \
+ do { message = MESSAGE; goto verify_error; } while (0)
+
+#define PUSH_PENDING(LABEL) \
+ do { if ((message = check_pending_block (LABEL)) != NULL) \
+ goto verify_error; } while (0)
+
+#ifdef __GNUC__
+#define CHECK_PC_IN_RANGE(PC) ({if (PC < 0 || PC > length) goto bad_pc; 1;})
+#else
+#define CHECK_PC_IN_RANGE(PC) (PC < 0 || PC > length ? \
+ (fatal("Bad byte codes.\n"), 0) : 1)
+#endif
+
+#define BCODE byte_ops
+
+/* Verify the bytecodes of the current method.
+ Return 1 on sucess, 0 on failure. */
+int
+verify_jvm_instructions (jcf, byte_ops, length)
+ JCF* jcf;
+ unsigned char* byte_ops;
+ long length;
+{
+ tree label;
+ int wide = 0;
+ int op_code;
+ int PC;
+ int oldpc; /* PC of start of instruction. */
+ int prevpc; /* If >= 0, PC of previous instruction. */
+ char *message;
+ int i;
+ register unsigned char *p;
+ struct eh_range *prev_eh_ranges = NULL_EH_RANGE;
+ struct eh_range *eh_ranges;
+
+ jint int_value = -1;
+
+ pending_blocks = NULL_TREE;
+
+ /* Handle the exception table. */
+ method_init_exceptions ();
+ JCF_SEEK (jcf, DECL_CODE_OFFSET (current_function_decl) + length);
+ i = JCF_readu2 (jcf);
+
+ /* We read the exception backwards. */
+ p = jcf->read_ptr + 8 * i;
+ while (--i >= 0)
+ {
+ int start_pc = GET_u2 (p-8);
+ int end_pc = GET_u2 (p-6);
+ int handler_pc = GET_u2 (p-4);
+ int catch_type = GET_u2 (p-2);
+ p -= 8;
+
+ if (start_pc < 0 || start_pc >= length
+ || end_pc < 0 || end_pc > length || start_pc >= end_pc
+ || handler_pc < 0 || handler_pc >= length
+ || (handler_pc >= start_pc && handler_pc < end_pc)
+ || ! (instruction_bits [start_pc] & BCODE_INSTRUCTION_START)
+ || ! (instruction_bits [end_pc] & BCODE_INSTRUCTION_START)
+ || ! (instruction_bits [handler_pc] & BCODE_INSTRUCTION_START))
+ {
+ error ("bad pc in exception_table");
+ return 0;
+ }
+
+ if (! add_handler (start_pc, end_pc,
+ lookup_label (handler_pc),
+ catch_type == 0 ? NULL_TREE
+ : get_class_constant (jcf, catch_type)))
+ {
+ error ("overlapping exception ranges are not supported");
+ return 0;
+ }
+
+ instruction_bits [handler_pc] |= BCODE_EXCEPTION_TARGET;
+ }
+
+ for (PC = 0;;)
+ {
+ int index;
+ tree type, tmp;
+ if (((PC != INVALID_PC
+ && instruction_bits [PC] & BCODE_TARGET) != 0)
+ || PC == 0)
+ {
+ PUSH_PENDING (lookup_label (PC));
+ INVALIDATE_PC;
+ }
+ if (PC == INVALID_PC)
+ {
+ label = pending_blocks;
+ if (label == NULL_TREE)
+ break; /* We're done! */
+ pending_blocks = LABEL_PENDING_CHAIN (label);
+ LABEL_CHANGED (label) = 0;
+
+ if (LABEL_IN_SUBR (label))
+ current_subr = LABEL_SUBR_START (label);
+ else
+ current_subr = NULL_TREE;
+
+ /* Restore type_map and stack_pointer from
+ LABEL_TYPE_STATE (label), and continue
+ compiling from there. */
+ load_type_state (label);
+ PC = LABEL_PC (label);
+ }
+ else if (PC >= length)
+ VERIFICATION_ERROR ("falling through end of method");
+
+ oldpc = PC;
+
+ if (!(instruction_bits [PC] & BCODE_INSTRUCTION_START) && ! wide)
+ VERIFICATION_ERROR ("PC not at instruction start");
+
+ instruction_bits[PC] |= BCODE_VERIFIED;
+
+ eh_ranges = find_handler (oldpc);
+
+ op_code = byte_ops[PC++];
+ switch (op_code)
+ {
+ int is_static, is_putting;
+ case OPCODE_nop:
+ break;
+ case OPCODE_iconst_m1:
+ case OPCODE_iconst_0: case OPCODE_iconst_1: case OPCODE_iconst_2:
+ case OPCODE_iconst_3: case OPCODE_iconst_4: case OPCODE_iconst_5:
+ i = op_code - OPCODE_iconst_0;
+ goto push_int;
+ push_int:
+ if (byte_ops[PC] == OPCODE_newarray
+ || byte_ops[PC] == OPCODE_newarray)
+ int_value = i;
+ push_type (int_type_node); break;
+ case OPCODE_lconst_0: case OPCODE_lconst_1:
+ push_type (long_type_node); break;
+ case OPCODE_fconst_0: case OPCODE_fconst_1: case OPCODE_fconst_2:
+ push_type (float_type_node); break;
+ case OPCODE_dconst_0: case OPCODE_dconst_1:
+ push_type (double_type_node); break;
+ case OPCODE_bipush:
+ i = IMMEDIATE_s1;
+ goto push_int;
+ case OPCODE_sipush:
+ i = IMMEDIATE_s2;
+ goto push_int;
+ case OPCODE_iload: type = int_type_node; goto general_load;
+ case OPCODE_lload: type = long_type_node; goto general_load;
+ case OPCODE_fload: type = float_type_node; goto general_load;
+ case OPCODE_dload: type = double_type_node; goto general_load;
+ case OPCODE_aload: type = ptr_type_node; goto general_load;
+ general_load:
+ index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
+ wide = 0;
+ goto load;
+ case OPCODE_iload_0: type = int_type_node; index = 0; goto load;
+ case OPCODE_iload_1: type = int_type_node; index = 1; goto load;
+ case OPCODE_iload_2: type = int_type_node; index = 2; goto load;
+ case OPCODE_iload_3: type = int_type_node; index = 3; goto load;
+ case OPCODE_lload_0: type = long_type_node; index = 0; goto load;
+ case OPCODE_lload_1: type = long_type_node; index = 1; goto load;
+ case OPCODE_lload_2: type = long_type_node; index = 2; goto load;
+ case OPCODE_lload_3: type = long_type_node; index = 3; goto load;
+ case OPCODE_fload_0: type = float_type_node; index = 0; goto load;
+ case OPCODE_fload_1: type = float_type_node; index = 1; goto load;
+ case OPCODE_fload_2: type = float_type_node; index = 2; goto load;
+ case OPCODE_fload_3: type = float_type_node; index = 3; goto load;
+ case OPCODE_dload_0: type = double_type_node; index = 0; goto load;
+ case OPCODE_dload_1: type = double_type_node; index = 1; goto load;
+ case OPCODE_dload_2: type = double_type_node; index = 2; goto load;
+ case OPCODE_dload_3: type = double_type_node; index = 3; goto load;
+ case OPCODE_aload_0: type = ptr_type_node; index = 0; goto load;
+ case OPCODE_aload_1: type = ptr_type_node; index = 1; goto load;
+ case OPCODE_aload_2: type = ptr_type_node; index = 2; goto load;
+ case OPCODE_aload_3: type = ptr_type_node; index = 3; goto load;
+ load:
+ if (index < 0
+ || (index + TYPE_IS_WIDE (type)
+ >= DECL_MAX_LOCALS (current_function_decl)))
+ VERIFICATION_ERROR ("invalid local variable index in load");
+ tmp = type_map[index];
+ if (tmp == TYPE_UNKNOWN || tmp == TYPE_SECOND
+ || (TYPE_IS_WIDE (type)
+ && type_map[index+1] != void_type_node)
+ || (type == ptr_type_node
+ ? TREE_CODE (tmp) != POINTER_TYPE
+ : type == int_type_node
+ ? (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
+ : type != tmp))
+ VERIFICATION_ERROR("invalid local variable type in load");
+ push_type (tmp);
+ goto note_used;
+ case OPCODE_istore: type = int_type_node; goto general_store;
+ case OPCODE_lstore: type = long_type_node; goto general_store;
+ case OPCODE_fstore: type = float_type_node; goto general_store;
+ case OPCODE_dstore: type = double_type_node; goto general_store;
+ case OPCODE_astore: type = ptr_type_node; goto general_store;
+ general_store:
+ index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
+ wide = 0;
+ goto store;
+ case OPCODE_istore_0: type = int_type_node; index = 0; goto store;
+ case OPCODE_istore_1: type = int_type_node; index = 1; goto store;
+ case OPCODE_istore_2: type = int_type_node; index = 2; goto store;
+ case OPCODE_istore_3: type = int_type_node; index = 3; goto store;
+ case OPCODE_lstore_0: type = long_type_node; index=0; goto store;
+ case OPCODE_lstore_1: type = long_type_node; index=1; goto store;
+ case OPCODE_lstore_2: type = long_type_node; index=2; goto store;
+ case OPCODE_lstore_3: type = long_type_node; index=3; goto store;
+ case OPCODE_fstore_0: type=float_type_node; index=0; goto store;
+ case OPCODE_fstore_1: type=float_type_node; index=1; goto store;
+ case OPCODE_fstore_2: type=float_type_node; index=2; goto store;
+ case OPCODE_fstore_3: type=float_type_node; index=3; goto store;
+ case OPCODE_dstore_0: type=double_type_node; index=0; goto store;
+ case OPCODE_dstore_1: type=double_type_node; index=1; goto store;
+ case OPCODE_dstore_2: type=double_type_node; index=2; goto store;
+ case OPCODE_dstore_3: type=double_type_node; index=3; goto store;
+ case OPCODE_astore_0: type = ptr_type_node; index = 0; goto store;
+ case OPCODE_astore_1: type = ptr_type_node; index = 1; goto store;
+ case OPCODE_astore_2: type = ptr_type_node; index = 2; goto store;
+ case OPCODE_astore_3: type = ptr_type_node; index = 3; goto store;
+ store:
+ if (index < 0
+ || (index + TYPE_IS_WIDE (type)
+ >= DECL_MAX_LOCALS (current_function_decl)))
+ {
+ VERIFICATION_ERROR ("invalid local variable index in store");
+ return 0;
+ }
+ type = pop_type (type);
+ type_map[index] = type;
+
+ /* If local variable changed, we need to reconsider eh handlers. */
+ prev_eh_ranges = NULL_EH_RANGE;
+
+ /* Allocate decl and rtx for this variable now, so if we're not
+ optmizing, we get a temporary that survives the whole method. */
+ find_local_variable (index, type, oldpc);
+
+ if (TYPE_IS_WIDE (type))
+ type_map[index+1] = TYPE_SECOND;
+ /* ... fall through to note_used ... */
+ note_used:
+ /* For store or load, note that local variable INDEX is used.
+ This is needed to verify try-finally sub-routines. */
+ if (current_subr)
+ {
+ tree vec = LABEL_RETURN_TYPE_STATE (current_subr);
+ tree subr_vec = LABEL_TYPE_STATE (current_subr);
+ int len = 1 + TYPE_IS_WIDE (type);
+ while (--len >= 0)
+ {
+ if (TREE_VEC_ELT (vec, index) == TYPE_UNUSED)
+ TREE_VEC_ELT (vec, index) = TREE_VEC_ELT (subr_vec, index);
+ }
+ }
+ break;
+ case OPCODE_iadd:
+ case OPCODE_iand:
+ case OPCODE_idiv:
+ case OPCODE_imul:
+ case OPCODE_ior:
+ case OPCODE_irem:
+ case OPCODE_ishl:
+ case OPCODE_ishr:
+ case OPCODE_isub:
+ case OPCODE_iushr:
+ case OPCODE_ixor:
+ type = int_type_node; goto binop;
+ case OPCODE_ineg:
+ case OPCODE_i2c:
+ case OPCODE_i2b:
+ case OPCODE_i2s:
+ type = int_type_node; goto unop;
+ case OPCODE_ladd:
+ case OPCODE_land:
+ case OPCODE_ldiv:
+ case OPCODE_lsub:
+ case OPCODE_lmul:
+ case OPCODE_lrem:
+ case OPCODE_lor:
+ case OPCODE_lxor:
+ type = long_type_node; goto binop;
+ case OPCODE_lneg:
+ type = long_type_node; goto unop;
+ case OPCODE_fadd: case OPCODE_fsub:
+ case OPCODE_fmul: case OPCODE_fdiv: case OPCODE_frem:
+ type = float_type_node; goto binop;
+ case OPCODE_fneg:
+ type = float_type_node; goto unop;
+ case OPCODE_dadd: case OPCODE_dsub:
+ case OPCODE_dmul: case OPCODE_ddiv: case OPCODE_drem:
+ type = double_type_node; goto binop;
+ case OPCODE_dneg:
+ type = double_type_node; goto unop;
+ unop:
+ pop_type (type);
+ push_type (type);
+ break;
+ binop:
+ pop_type (type);
+ pop_type (type);
+ push_type (type);
+ break;
+ case OPCODE_lshl:
+ case OPCODE_lshr:
+ case OPCODE_lushr:
+ pop_type (int_type_node);
+ pop_type (long_type_node);
+ push_type (long_type_node);
+ break;
+ case OPCODE_iinc:
+ index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
+ PC += wide + 1;
+ wide = 0;
+ if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl))
+ VERIFICATION_ERROR ("invalid local variable index in iinc");
+ tmp = type_map[index];
+ if (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
+ VERIFICATION_ERROR ("invalid local variable type in iinc");
+ break;
+ case OPCODE_i2l:
+ pop_type (int_type_node); push_type (long_type_node); break;
+ case OPCODE_i2f:
+ pop_type (int_type_node); push_type (float_type_node); break;
+ case OPCODE_i2d:
+ pop_type (int_type_node); push_type (double_type_node); break;
+ case OPCODE_l2i:
+ pop_type (long_type_node); push_type (int_type_node); break;
+ case OPCODE_l2f:
+ pop_type (long_type_node); push_type (float_type_node); break;
+ case OPCODE_l2d:
+ pop_type (long_type_node); push_type (double_type_node); break;
+ case OPCODE_f2i:
+ pop_type (float_type_node); push_type (int_type_node); break;
+ case OPCODE_f2l:
+ pop_type (float_type_node); push_type (long_type_node); break;
+ case OPCODE_f2d:
+ pop_type (float_type_node); push_type (double_type_node); break;
+ case OPCODE_d2i:
+ pop_type (double_type_node); push_type (int_type_node); break;
+ case OPCODE_d2l:
+ pop_type (double_type_node); push_type (long_type_node); break;
+ case OPCODE_d2f:
+ pop_type (double_type_node); push_type (float_type_node); break;
+ case OPCODE_lcmp:
+ type = long_type_node; goto compare;
+ case OPCODE_fcmpl:
+ case OPCODE_fcmpg:
+ type = float_type_node; goto compare;
+ case OPCODE_dcmpl:
+ case OPCODE_dcmpg:
+ type = double_type_node; goto compare;
+ compare:
+ pop_type (type); pop_type (type);
+ push_type (int_type_node); break;
+ case OPCODE_ifeq:
+ case OPCODE_ifne:
+ case OPCODE_iflt:
+ case OPCODE_ifge:
+ case OPCODE_ifgt:
+ case OPCODE_ifle:
+ pop_type (int_type_node); goto cond;
+ case OPCODE_ifnull:
+ case OPCODE_ifnonnull:
+ pop_type (ptr_type_node ); goto cond;
+ case OPCODE_if_icmpeq:
+ case OPCODE_if_icmpne:
+ case OPCODE_if_icmplt:
+ case OPCODE_if_icmpge:
+ case OPCODE_if_icmpgt:
+ case OPCODE_if_icmple:
+ pop_type (int_type_node); pop_type (int_type_node); goto cond;
+ case OPCODE_if_acmpeq:
+ case OPCODE_if_acmpne:
+ pop_type (object_ptr_type_node); pop_type (object_ptr_type_node);
+ goto cond;
+ cond:
+ PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
+ break;
+ case OPCODE_goto:
+ PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
+ INVALIDATE_PC;
+ break;
+ case OPCODE_wide:
+ switch (byte_ops[PC])
+ {
+ case OPCODE_iload: case OPCODE_lload:
+ case OPCODE_fload: case OPCODE_dload: case OPCODE_aload:
+ case OPCODE_istore: case OPCODE_lstore:
+ case OPCODE_fstore: case OPCODE_dstore: case OPCODE_astore:
+ case OPCODE_iinc:
+ case OPCODE_ret:
+ wide = 1;
+ break;
+ default:
+ VERIFICATION_ERROR ("invalid use of wide instruction");
+ }
+ break;
+ case OPCODE_ireturn: type = int_type_node; goto ret;
+ case OPCODE_lreturn: type = long_type_node; goto ret;
+ case OPCODE_freturn: type = float_type_node; goto ret;
+ case OPCODE_dreturn: type = double_type_node; goto ret;
+ case OPCODE_areturn: type = ptr_type_node; goto ret;
+ ret:
+ pop_type (type);
+ /* ... fall through ... */
+ case OPCODE_return:
+ INVALIDATE_PC;
+ break;
+ case OPCODE_getstatic: is_putting = 0; is_static = 1; goto field;
+ case OPCODE_putstatic: is_putting = 1; is_static = 1; goto field;
+ case OPCODE_getfield: is_putting = 0; is_static = 0; goto field;
+ case OPCODE_putfield: is_putting = 1; is_static = 0; goto field;
+ field:
+ {
+ int index = IMMEDIATE_u2;
+ tree self_type = get_class_constant
+ (jcf, COMPONENT_REF_CLASS_INDEX (&current_jcf->cpool, index));
+ tree field_name = COMPONENT_REF_NAME (&current_jcf->cpool, index);
+ tree field_signature = COMPONENT_REF_SIGNATURE (&current_jcf->cpool, index);
+ tree field_type = get_type_from_signature (field_signature);
+ if (is_putting)
+ pop_type (field_type);
+ if (! is_static)
+ {
+ /* Defer actual checking until next pass. */
+ pop_type (ptr_type_node);
+ }
+ if (! is_putting)
+ push_type (field_type);
+ break;
+ }
+ case OPCODE_new:
+ push_type (get_class_constant (jcf, IMMEDIATE_u2));
+ break;
+ case OPCODE_dup: type_stack_dup (1, 0); break;
+ case OPCODE_dup_x1: type_stack_dup (1, 1); break;
+ case OPCODE_dup_x2: type_stack_dup (1, 2); break;
+ case OPCODE_dup2: type_stack_dup (2, 0); break;
+ case OPCODE_dup2_x1: type_stack_dup (2, 1); break;
+ case OPCODE_dup2_x2: type_stack_dup (2, 2); break;
+ case OPCODE_pop: index = 1; goto pop;
+ case OPCODE_pop2: index = 2; goto pop;
+ pop:
+ if (stack_pointer < index)
+ VERIFICATION_ERROR ("stack underflow");
+ stack_pointer -= index;
+ break;
+ case OPCODE_swap:
+ if (stack_pointer < 2)
+ VERIFICATION_ERROR ("stack underflow (in swap)");
+ else
+ {
+ tree type1 = stack_type_map[stack_pointer - 1];
+ tree type2 = stack_type_map[stack_pointer - 2];
+ if (type1 == void_type_node || type2 == void_type_node)
+ VERIFICATION_ERROR ("verifier (swap): double or long value");
+ stack_type_map[stack_pointer - 2] = type1;
+ stack_type_map[stack_pointer - 1] = type2;
+ }
+ break;
+ case OPCODE_ldc: index = IMMEDIATE_u1; goto ldc;
+ case OPCODE_ldc2_w:
+ case OPCODE_ldc_w:
+ index = IMMEDIATE_u2; goto ldc;
+ ldc:
+ if (index <= 0 || index >= JPOOL_SIZE(current_jcf))
+ VERIFICATION_ERROR ("bad constant pool index in ldc");
+ int_value = -1;
+ switch (JPOOL_TAG (current_jcf, index) & ~CONSTANT_ResolvedFlag)
+ {
+ case CONSTANT_Integer: type = int_type_node; goto check_ldc;
+ case CONSTANT_Float: type = float_type_node; goto check_ldc;
+ case CONSTANT_String: type = string_type_node; goto check_ldc;
+ case CONSTANT_Long: type = long_type_node; goto check_ldc;
+ case CONSTANT_Double: type = double_type_node; goto check_ldc;
+ check_ldc:
+ if (TYPE_IS_WIDE (type) == (op_code == OPCODE_ldc2_w))
+ break;
+ /* ... else fall through ... */
+ default:
+ bad_ldc:
+ VERIFICATION_ERROR ("bad constant pool tag in ldc");
+ }
+ if (type == int_type_node)
+ {
+ i = TREE_INT_CST_LOW (get_constant (current_jcf, index));
+ goto push_int;
+ }
+ push_type (type);
+ break;
+
+ case OPCODE_invokevirtual:
+ case OPCODE_invokespecial:
+ case OPCODE_invokestatic:
+ case OPCODE_invokeinterface:
+ {
+ int index = IMMEDIATE_u2;
+ tree sig = COMPONENT_REF_SIGNATURE (&current_jcf->cpool, index);
+ tree self_type = get_class_constant
+ (current_jcf, COMPONENT_REF_CLASS_INDEX (&current_jcf->cpool,
+ index));
+ tree method_name = COMPONENT_REF_NAME (&current_jcf->cpool, index);
+ tree method_type;
+ method_type = parse_signature_string (IDENTIFIER_POINTER (sig),
+ IDENTIFIER_LENGTH (sig));
+ if (TREE_CODE (method_type) != FUNCTION_TYPE)
+ VERIFICATION_ERROR ("bad method signature");
+ pop_argument_types (TYPE_ARG_TYPES (method_type));
+
+ /* Can't invoke <clinit> */
+ if (method_name == clinit_identifier_node)
+ VERIFICATION_ERROR ("invoke opcode can't invoke <clinit>");
+ /* Apart invokespecial, can't invoke <init> */
+ if (op_code != OPCODE_invokespecial
+ && method_name == init_identifier_node)
+ VERIFICATION_ERROR ("invoke opcode can't invoke <init>");
+
+ if (op_code != OPCODE_invokestatic)
+ pop_type (self_type);
+
+ switch (op_code)
+ {
+ case OPCODE_invokeinterface:
+ {
+ int nargs = IMMEDIATE_u1;
+ int notZero = IMMEDIATE_u1;
+
+ if (!nargs || notZero)
+ VERIFICATION_ERROR
+ ("invalid argument number in invokeinterface");
+ break;
+ }
+ }
+
+ if (TREE_TYPE (method_type) != void_type_node)
+ push_type (TREE_TYPE (method_type));
+ break;
+ }
+
+ case OPCODE_arraylength:
+ /* Type checking actually made during code generation */
+ pop_type( ptr_type_node );
+ push_type( int_type_node );
+ break;
+
+ /* Q&D verification *or* more checking done during code generation
+ for byte/boolean/char/short, the value popped is a int coerced
+ into the right type before being stored. */
+ case OPCODE_iastore: type = int_type_node; goto astore;
+ case OPCODE_lastore: type = long_type_node; goto astore;
+ case OPCODE_fastore: type = float_type_node; goto astore;
+ case OPCODE_dastore: type = double_type_node; goto astore;
+ case OPCODE_aastore: type = ptr_type_node; goto astore;
+ case OPCODE_bastore: type = int_type_node; goto astore;
+ case OPCODE_castore: type = int_type_node; goto astore;
+ case OPCODE_sastore: type = int_type_node; goto astore;
+ astore:
+ /* FIXME - need better verification here */
+ pop_type (type); /* new value */
+ pop_type (int_type_node); /* index */
+ pop_type (ptr_type_node); /* array */
+ break;
+
+ /* Q&D verification *or* more checking done during code generation
+ for byte/boolean/char/short, the value pushed is a int. */
+ case OPCODE_iaload: type = int_type_node; goto aload;
+ case OPCODE_laload: type = long_type_node; goto aload;
+ case OPCODE_faload: type = float_type_node; goto aload;
+ case OPCODE_daload: type = double_type_node; goto aload;
+ case OPCODE_aaload: type = ptr_type_node; goto aload;
+ case OPCODE_baload: type = promote_type (byte_type_node); goto aload;
+ case OPCODE_caload: type = promote_type (char_type_node); goto aload;
+ case OPCODE_saload: type = promote_type (short_type_node); goto aload;
+ aload:
+ pop_type (int_type_node);
+ type = pop_type (ptr_type_node);
+ if (! is_array_type_p (type))
+ VERIFICATION_ERROR ("array load from non-array type");
+ push_type (TYPE_ARRAY_ELEMENT (TREE_TYPE (type)));
+ break;
+
+ case OPCODE_anewarray:
+ type = get_class_constant (current_jcf, IMMEDIATE_u2);
+ type = promote_type (type);
+ goto newarray;
+
+ case OPCODE_newarray:
+ index = IMMEDIATE_u1;
+ type = decode_newarray_type (index);
+ if (type == NULL_TREE)
+ VERIFICATION_ERROR ("invalid type code in newarray opcode");
+ goto newarray;
+
+ newarray:
+ if (int_value >= 0 && prevpc >= 0)
+ {
+ /* If previous instruction pushed int constant,
+ we want to use it. */
+ switch (byte_ops[prevpc])
+ {
+ case OPCODE_iconst_0: case OPCODE_iconst_1:
+ case OPCODE_iconst_2: case OPCODE_iconst_3:
+ case OPCODE_iconst_4: case OPCODE_iconst_5:
+ case OPCODE_bipush: case OPCODE_sipush:
+ case OPCODE_ldc: case OPCODE_ldc_w:
+ break;
+ default:
+ int_value = -1;
+ }
+ }
+ else
+ int_value = -1;
+ type = build_java_array_type (type, int_value);
+ pop_type (int_type_node);
+ push_type (type);
+ break;
+
+ case OPCODE_multianewarray:
+ {
+ int ndim, i;
+ index = IMMEDIATE_u2;
+ ndim = IMMEDIATE_u1;
+
+ if( ndim < 1 )
+ VERIFICATION_ERROR ("number of dimension lower that 1 in multianewarray" );
+
+ for( i = 0; i < ndim; i++ )
+ pop_type (int_type_node);
+ push_type (get_class_constant (current_jcf, index));
+ break;
+ }
+
+ case OPCODE_aconst_null:
+ push_type (ptr_type_node);
+ break;
+
+ case OPCODE_athrow:
+ pop_type (throwable_type_node);
+ INVALIDATE_PC;
+ break;
+
+ case OPCODE_checkcast:
+ pop_type (ptr_type_node);
+ type = get_class_constant (current_jcf, IMMEDIATE_u2);
+ push_type (type);
+ break;
+ case OPCODE_instanceof:
+ pop_type (ptr_type_node);
+ get_class_constant (current_jcf, IMMEDIATE_u2);
+ push_type (integer_type_node);
+ break;
+
+ case OPCODE_tableswitch:
+ {
+ jint default_val, low, high;
+
+ pop_type (integer_type_node);
+ while (PC%4)
+ {
+ if (byte_ops[PC++])
+ VERIFICATION_ERROR ("bad alignment in tableswitch pad");
+ }
+ PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4));
+ low = IMMEDIATE_s4;
+ high = IMMEDIATE_s4;
+
+ if (low > high)
+ VERIFICATION_ERROR ("unsorted low/high value in tableswitch");
+
+ while (low++ <= high)
+ PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
+ break;
+ }
+
+ case OPCODE_lookupswitch:
+ {
+ jint npairs, last, not_registered = 1;
+
+ pop_type (integer_type_node);
+ while (PC%4)
+ {
+ if (byte_ops[PC++])
+ VERIFICATION_ERROR ("bad alignment in lookupswitch pad");
+ }
+
+ PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4));
+ npairs = IMMEDIATE_s4;
+
+ if (npairs < 0)
+ VERIFICATION_ERROR ("invalid number of targets in lookupswitch");
+
+ while (npairs--)
+ {
+ int match = IMMEDIATE_s4;
+ if (not_registered)
+ not_registered = 0;
+ else if (last >= match)
+ VERIFICATION_ERROR ("unsorted match value in lookupswitch");
+
+ last = match;
+ PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
+ }
+ break;
+ }
+
+ case OPCODE_monitorenter:
+ /* fall thru */
+ case OPCODE_monitorexit:
+ pop_type (ptr_type_node);
+ break;
+
+ case OPCODE_goto_w:
+ PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
+ INVALIDATE_PC;
+ break;
+
+ case OPCODE_jsr:
+ {
+ tree target = lookup_label (oldpc + IMMEDIATE_s2);
+ tree return_label = lookup_label (PC);
+ push_type (return_address_type_node);
+ if (! LABEL_VERIFIED (target))
+ {
+ /* first time seen */
+ tree return_type_map;
+ int nlocals = DECL_MAX_LOCALS (current_function_decl);
+ index = nlocals + DECL_MAX_STACK (current_function_decl);
+ return_type_map = make_tree_vec (index);
+ while (--index >= nlocals)
+ TREE_VEC_ELT (return_type_map, index) = TYPE_UNKNOWN;
+ while (--index >= 0)
+ TREE_VEC_ELT (return_type_map, index) = TYPE_UNUSED;
+ LABEL_RETURN_LABEL (target)
+ = build_decl (LABEL_DECL, NULL_TREE, TREE_TYPE (target));
+ LABEL_PC (LABEL_RETURN_LABEL (target)) = -1;
+ LABEL_RETURN_TYPE_STATE (target) = return_type_map;
+ LABEL_IS_SUBR_START (target) = 1;
+ LABEL_IN_SUBR (target) = 1;
+ LABEL_SUBR_START (target) = target;
+ LABEL_SUBR_CONTEXT (target) = current_subr;
+ }
+ else if (! LABEL_IS_SUBR_START (target)
+ || LABEL_SUBR_CONTEXT (target) != current_subr)
+ VERIFICATION_ERROR ("label part of different subroutines");
+
+ i = merge_type_state (target);
+ if (i != 0)
+ {
+ if (i < 0)
+ VERIFICATION_ERROR ("types could not be merged at jsr");
+ push_pending_label (target);
+ }
+ current_subr = target;
+
+ /* Chain return_pc onto LABEL_RETURN_LABELS (target) if needed. */
+ if (! value_member (return_label, LABEL_RETURN_LABELS (target)))
+ {
+ LABEL_RETURN_LABELS (target)
+ = tree_cons (NULL_TREE, return_label,
+ LABEL_RETURN_LABELS (target));
+ }
+
+ if (LABEL_VERIFIED (target))
+ {
+ tree return_map = LABEL_RETURN_TYPE_STATE (target);
+ int len = TREE_VEC_LENGTH (return_map);
+ stack_pointer = len - DECL_MAX_LOCALS (current_function_decl);
+ while (--len >= 0)
+ {
+ if (TREE_VEC_ELT (return_map, len) != TYPE_UNUSED)
+ type_map[len] = TREE_VEC_ELT (return_map, len);
+ }
+ current_subr = LABEL_SUBR_CONTEXT (target);
+ PUSH_PENDING (return_label);
+ }
+
+ INVALIDATE_PC;
+ }
+ break;
+ case OPCODE_ret:
+ if (current_subr == NULL)
+ VERIFICATION_ERROR ("ret instruction not in a jsr subroutine");
+ else
+ {
+ tree ret_map = LABEL_RETURN_TYPE_STATE (current_subr);
+ tree caller = LABEL_SUBR_CONTEXT (current_subr);
+ int size = DECL_MAX_LOCALS(current_function_decl)+stack_pointer;
+ index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
+ wide = 0;
+ INVALIDATE_PC;
+ if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl)
+ || type_map[index] != TYPE_RETURN_ADDR)
+ VERIFICATION_ERROR ("invalid ret index");
+
+ /* The next chunk of code is similar to an inlined version of
+ * merge_type_state (LABEL_RETURN_LABEL (current_subr)).
+ * The main differences are that LABEL_RETURN_LABEL is
+ * pre-allocated by the jsr (but we don't know the size then);
+ * and that we have to handle TYPE_UNUSED. */
+
+ if (! RETURN_MAP_ADJUSTED (ret_map))
+ { /* First return from this subroutine - fix stack pointer. */
+ TREE_VEC_LENGTH (ret_map) = size;
+ for (index = size; --index >= 0; )
+ {
+ if (TREE_VEC_ELT (ret_map, index) != TYPE_UNUSED)
+ TREE_VEC_ELT (ret_map, index) = type_map[index];
+ }
+ RETURN_MAP_ADJUSTED (ret_map) = 1;
+ }
+ else
+ {
+ if (TREE_VEC_LENGTH (ret_map) != size)
+ VERIFICATION_ERROR ("inconsistent stack size on ret");
+ for (index = 0; index < size; index++)
+ {
+ tree type = TREE_VEC_ELT (ret_map, index);
+ if (type != TYPE_UNUSED)
+ {
+ type = merge_types (type, type_map [index]);
+ TREE_VEC_ELT (ret_map, index) = type;
+ if (type == TYPE_UNKNOWN)
+ {
+ if (index >= size - stack_pointer)
+ VERIFICATION_ERROR
+ ("inconsistent types on ret from jsr");
+ }
+ else if (TYPE_IS_WIDE (type))
+ index++;
+ }
+ }
+ }
+
+ /* Check if there are any more pending blocks in this subroutine.
+ Because we push pending blocks in a last-in-first-out order,
+ and because we don't push anything from our caller until we
+ are done with this subroutine or anything nested in it,
+ then we are done if the top of the pending_blocks stack is
+ not in a subroutine, or it is in our caller. */
+ if (pending_blocks == NULL_TREE
+ || ! LABEL_IN_SUBR (pending_blocks)
+ || LABEL_SUBR_START (pending_blocks) == caller)
+ {
+ /* Since we are done with this subroutine (i.e. this is the
+ last ret from it), set up the (so far known) return
+ address as pending - with the merged type state. */
+ tmp = LABEL_RETURN_LABELS (current_subr);
+ current_subr = caller;
+ for ( ; tmp != NULL_TREE; tmp = TREE_CHAIN (tmp))
+ {
+ tree return_label = TREE_VALUE (tmp);
+ tree return_state = LABEL_TYPE_STATE (return_label);
+ if (return_state == NULL_TREE)
+ {
+ /* This means means we had not verified the
+ subroutine earlier, so this is the first jsr to
+ call it. In this case, the type_map of the return
+ address is just the current type_map - and that
+ is handled by the following PUSH_PENDING. */
+ }
+ else
+ {
+ /* In this case we have to do a merge. But first
+ restore the type_map for unused slots to those
+ that were in effect at the jsr. */
+ for (index = size; --index >= 0; )
+ {
+ type_map[index] = TREE_VEC_ELT (ret_map, index);
+ if (type_map[index] == TYPE_UNUSED)
+ type_map[index]
+ = TREE_VEC_ELT (return_state, index);
+ }
+ }
+ PUSH_PENDING (return_label);
+ }
+ }
+ }
+ break;
+ case OPCODE_jsr_w:
+ case OPCODE_ret_w:
+ default:
+ error ("unknown opcode %d@pc=%d during verification", op_code, PC-1);
+ return 0;
+ }
+
+ prevpc = oldpc;
+
+ /* The following test is true if we have entered or exited an exception
+ handler range *or* we have done a store to a local variable.
+ In either case we need to consider any exception handlers that
+ might "follow" this instruction. */
+
+ if (eh_ranges != prev_eh_ranges)
+ {
+ int save_stack_pointer = stack_pointer;
+ int index = DECL_MAX_LOCALS (current_function_decl);
+ tree save_type = type_map[index];
+ tree save_current_subr = current_subr;
+ struct eh_range *ranges = find_handler (oldpc);
+ stack_pointer = 1;
+ for (; ranges != NULL_EH_RANGE; ranges = ranges->outer)
+ {
+ tree chain = ranges->handlers;
+
+ /* We need to determine if the handler is part of current_subr.
+ The are two cases: (1) The exception catch range
+ is entirely within current_subr. In that case the handler
+ is also part of current_subr.
+ (2) Some of the catch range is not in current_subr.
+ In that case, the handler is *not* part of current_subr.
+
+ Figuring out which is the case is not necessarily obvious,
+ in the presence of clever code generators (and obfuscators).
+ We make a simplifying assumption that in case (2) we
+ have that the current_subr is entirely within the catch range.
+ In that case we can assume if that if a caller (the jsr) of
+ a subroutine is within the catch range, then the handler is
+ *not* part of the subroutine, and vice versa. */
+
+ current_subr = save_current_subr;
+ for ( ; current_subr != NULL_TREE;
+ current_subr = LABEL_SUBR_CONTEXT (current_subr))
+ {
+ tree return_labels = LABEL_RETURN_LABELS (current_subr);
+ /* There could be multiple return_labels, but
+ we only need to check one. */
+ int return_pc = LABEL_PC (TREE_VALUE (return_labels));
+ if (return_pc <= ranges->start_pc
+ || return_pc > ranges->end_pc)
+ break;
+ }
+
+ for ( ; chain != NULL_TREE; chain = TREE_CHAIN (chain))
+ {
+ tree handler = TREE_VALUE (chain);
+ tree type = TREE_PURPOSE (chain);
+ if (type == NULL_TREE) /* a finally handler */
+ type = throwable_type_node;
+ type_map[index] = promote_type (type);
+
+ PUSH_PENDING (handler);
+ }
+ }
+ stack_pointer = save_stack_pointer;
+ current_subr = save_current_subr;
+ type_map[index] = save_type;
+ prev_eh_ranges = eh_ranges;
+ }
+ }
+ return 1;
+ bad_pc:
+ message = "program counter out of range";
+ goto verify_error;
+ verify_error:
+ error ("verification error at PC=%d: %s", oldpc, message);
+ return 0;
+}