changeset 14892:75104db5215e

Move Bytecode parsing into BytecodeParseHelper (defunc).
author Josef Eisl <josef.eisl@jku.at>
date Mon, 24 Mar 2014 16:16:31 +0100
parents dbe762fc0eb1
children f97c85e6366d
files graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParseHelper.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java
diffstat 2 files changed, 1813 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParseHelper.java	Mon Mar 24 13:56:56 2014 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParseHelper.java	Mon Mar 24 16:16:31 2014 +0100
@@ -1,13 +1,40 @@
 package com.oracle.graal.java;
 
 import static com.oracle.graal.bytecode.Bytecodes.*;
+import static java.lang.reflect.Modifier.*;
 
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.ProfilingInfo.TriState;
+import com.oracle.graal.api.meta.ResolvedJavaType.Representation;
+import com.oracle.graal.bytecode.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.java.BciBlockMapping.BciBlock;
+import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
+import com.oracle.graal.nodes.cfg.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
 
-public class BytecodeParseHelper<T extends KindInterface> {
+public abstract class BytecodeParseHelper<T extends KindInterface> {
 
     private AbstractFrameStateBuilder<T> frameState;
+    private BytecodeStream stream;           // the bytecode stream
+    private GraphBuilderConfiguration graphBuilderConfig;
+    private ResolvedJavaType method;
+    private AbstractBlock<?> currentBlock;
+
+    public BytecodeParseHelper() {
+    }
 
     public BytecodeParseHelper(AbstractFrameStateBuilder<T> frameState) {
         this.frameState = frameState;
@@ -17,11 +44,19 @@
         this.frameState = frameState;
     }
 
+    public final void setStream(BytecodeStream stream) {
+        this.stream = stream;
+    }
+
+    private BytecodeStream getStream() {
+        return stream;
+    }
+
     public void loadLocal(int index, Kind kind) {
         frameState.push(kind, frameState.loadLocal(index));
     }
 
-    public void storeLocal(Kind kind, int index) {
+    private void storeLocal(Kind kind, int index) {
         T value;
         if (kind == Kind.Object) {
             value = frameState.xpop();
@@ -33,7 +68,104 @@
         frameState.storeLocal(index, value);
     }
 
-    public void stackOp(int opcode) {
+    /**
+     * @param type the unresolved type of the constant
+     */
+    protected abstract void handleUnresolvedLoadConstant(JavaType type);
+
+    /**
+     * @param type the unresolved type of the type check
+     * @param object the object value whose type is being checked against {@code type}
+     */
+    protected abstract void handleUnresolvedCheckCast(JavaType type, T object);
+
+    /**
+     * @param type the unresolved type of the type check
+     * @param object the object value whose type is being checked against {@code type}
+     */
+    protected abstract void handleUnresolvedInstanceOf(JavaType type, T object);
+
+    /**
+     * @param type the type being instantiated
+     */
+    protected abstract void handleUnresolvedNewInstance(JavaType type);
+
+    /**
+     * @param type the type of the array being instantiated
+     * @param length the length of the array
+     */
+    protected abstract void handleUnresolvedNewObjectArray(JavaType type, T length);
+
+    /**
+     * @param type the type being instantiated
+     * @param dims the dimensions for the multi-array
+     */
+    protected abstract void handleUnresolvedNewMultiArray(JavaType type, T[] dims);
+
+    /**
+     * @param field the unresolved field
+     * @param receiver the object containing the field or {@code null} if {@code field} is static
+     */
+    protected abstract void handleUnresolvedLoadField(JavaField field, T receiver);
+
+    /**
+     * @param field the unresolved field
+     * @param value the value being stored to the field
+     * @param receiver the object containing the field or {@code null} if {@code field} is static
+     */
+    protected abstract void handleUnresolvedStoreField(JavaField field, T value, T receiver);
+
+    /**
+     * @param representation
+     * @param type
+     */
+    protected abstract void handleUnresolvedExceptionType(Representation representation, JavaType type);
+
+    // protected abstract void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKind);
+
+    // protected abstract DispatchBeginNode handleException(T exceptionObject, int bci);
+
+    private void genLoadConstant(int cpi, int opcode) {
+        Object con = lookupConstant(cpi, opcode);
+
+        if (con instanceof JavaType) {
+            // this is a load of class constant which might be unresolved
+            JavaType type = (JavaType) con;
+            if (type instanceof ResolvedJavaType) {
+                frameState.push(Kind.Object, appendConstant(((ResolvedJavaType) type).getEncoding(Representation.JavaClass)));
+            } else {
+                handleUnresolvedLoadConstant(type);
+            }
+        } else if (con instanceof Constant) {
+            Constant constant = (Constant) con;
+            frameState.push(constant.getKind().getStackKind(), appendConstant(constant));
+        } else {
+            throw new Error("lookupConstant returned an object of incorrect type");
+        }
+    }
+
+    protected abstract T genLoadIndexed(T index, T array, Kind kind);
+
+    private void genLoadIndexed(Kind kind) {
+        emitExplicitExceptions(frameState.peek(1), frameState.peek(0));
+
+        T index = frameState.ipop();
+        T array = frameState.apop();
+        frameState.push(kind.getStackKind(), append(genLoadIndexed(array, index, kind)));
+    }
+
+    protected abstract T genStoreIndexed(T array, T index, Kind kind, T value);
+
+    private void genStoreIndexed(Kind kind) {
+        emitExplicitExceptions(frameState.peek(2), frameState.peek(1));
+
+        T value = frameState.pop(kind.getStackKind());
+        T index = frameState.ipop();
+        T array = frameState.apop();
+        append(genStoreIndexed(array, index, kind, value));
+    }
+
+    private void stackOp(int opcode) {
         switch (opcode) {
             case POP: {
                 frameState.xpop();
@@ -112,4 +244,1518 @@
                 throw GraalInternalError.shouldNotReachHere();
         }
     }
+
+    protected abstract T genIntegerAdd(Kind kind, T x, T y);
+
+    protected abstract T genIntegerSub(Kind kind, T x, T y);
+
+    protected abstract T genIntegerMul(Kind kind, T x, T y);
+
+    protected abstract T genFloatAdd(Kind kind, T x, T y, boolean isStrictFP);
+
+    protected abstract T genFloatSub(Kind kind, T x, T y, boolean isStrictFP);
+
+    protected abstract T genFloatMul(Kind kind, T x, T y, boolean isStrictFP);
+
+    protected abstract T genFloatDiv(Kind kind, T x, T y, boolean isStrictFP);
+
+    protected abstract T genFloatRem(Kind kind, T x, T y, boolean isStrictFP);
+
+    private void genArithmeticOp(Kind result, int opcode) {
+        T y = frameState.pop(result);
+        T x = frameState.pop(result);
+        boolean isStrictFP = isStrict(method.getModifiers());
+        T v;
+        switch (opcode) {
+            case IADD:
+            case LADD:
+                v = genIntegerAdd(result, x, y);
+                break;
+            case FADD:
+            case DADD:
+                v = genFloatAdd(result, x, y, isStrictFP);
+                break;
+            case ISUB:
+            case LSUB:
+                v = genIntegerSub(result, x, y);
+                break;
+            case FSUB:
+            case DSUB:
+                v = genFloatSub(result, x, y, isStrictFP);
+                break;
+            case IMUL:
+            case LMUL:
+                v = genIntegerMul(result, x, y);
+                break;
+            case FMUL:
+            case DMUL:
+                v = genFloatMul(result, x, y, isStrictFP);
+                break;
+            case FDIV:
+            case DDIV:
+                v = genFloatDiv(result, x, y, isStrictFP);
+                break;
+            case FREM:
+            case DREM:
+                v = genFloatRem(result, x, y, isStrictFP);
+                break;
+            default:
+                throw new GraalInternalError("should not reach");
+        }
+        frameState.push(result, append(v));
+    }
+
+    protected abstract T genIntegerDiv(Kind kind, T x, T y);
+
+    protected abstract T genIntegerRem(Kind kind, T x, T y);
+
+    private void genIntegerDivOp(Kind result, int opcode) {
+        T y = frameState.pop(result);
+        T x = frameState.pop(result);
+        T v;
+        switch (opcode) {
+            case IDIV:
+            case LDIV:
+                v = genIntegerDiv(result, x, y);
+                break;
+            case IREM:
+            case LREM:
+                v = genIntegerRem(result, x, y);
+                break;
+            default:
+                throw new GraalInternalError("should not reach");
+        }
+        frameState.push(result, append(v));
+    }
+
+    protected abstract T genNegateOp(T x);
+
+    private void genNegateOp(Kind kind) {
+        frameState.push(kind, append(genNegateOp(frameState.pop(kind))));
+    }
+
+    protected abstract T genLeftShift(Kind kind, T x, T y);
+
+    protected abstract T genRightShift(Kind kind, T x, T y);
+
+    protected abstract T genUnsignedRightShift(Kind kind, T x, T y);
+
+    private void genShiftOp(Kind kind, int opcode) {
+        T s = frameState.ipop();
+        T x = frameState.pop(kind);
+        T v;
+        switch (opcode) {
+            case ISHL:
+            case LSHL:
+                v = genLeftShift(kind, x, s);
+                break;
+            case ISHR:
+            case LSHR:
+                v = genRightShift(kind, x, s);
+                break;
+            case IUSHR:
+            case LUSHR:
+                v = genUnsignedRightShift(kind, x, s);
+                break;
+            default:
+                throw new GraalInternalError("should not reach");
+        }
+        frameState.push(kind, append(v));
+    }
+
+    protected abstract T genAnd(Kind kind, T x, T y);
+
+    protected abstract T genOr(Kind kind, T x, T y);
+
+    protected abstract T genXor(Kind kind, T x, T y);
+
+    private void genLogicOp(Kind kind, int opcode) {
+        T y = frameState.pop(kind);
+        T x = frameState.pop(kind);
+        T v;
+        switch (opcode) {
+            case IAND:
+            case LAND:
+                v = genAnd(kind, x, y);
+                break;
+            case IOR:
+            case LOR:
+                v = genOr(kind, x, y);
+                break;
+            case IXOR:
+            case LXOR:
+                v = genXor(kind, x, y);
+                break;
+            default:
+                throw new GraalInternalError("should not reach");
+        }
+        frameState.push(kind, append(v));
+    }
+
+    protected abstract T genNormalizeCompare(T x, T y, boolean isUnorderedLess);
+
+    private void genCompareOp(Kind kind, boolean isUnorderedLess) {
+        T y = frameState.pop(kind);
+        T x = frameState.pop(kind);
+        frameState.ipush(append(genNormalizeCompare(x, y, isUnorderedLess)));
+    }
+
+    protected abstract T genFloatConvert(FloatConvert op, T input);
+
+    private void genFloatConvert(FloatConvert op, Kind from, Kind to) {
+        T input = frameState.pop(from.getStackKind());
+        frameState.push(to.getStackKind(), append(genFloatConvert(op, input)));
+    }
+
+    protected abstract T genNarrow(T input, int bitCount);
+
+    protected abstract T genSignExtend(T input, int bitCount);
+
+    protected abstract T genZeroExtend(T input, int bitCount);
+
+    private void genSignExtend(Kind from, Kind to) {
+        T input = frameState.pop(from.getStackKind());
+        if (from != from.getStackKind()) {
+            input = append(genNarrow(input, from.getBitCount()));
+        }
+        frameState.push(to.getStackKind(), append(genSignExtend(input, to.getBitCount())));
+    }
+
+    private void genZeroExtend(Kind from, Kind to) {
+        T input = frameState.pop(from.getStackKind());
+        if (from != from.getStackKind()) {
+            input = append(genNarrow(input, from.getBitCount()));
+        }
+        frameState.push(to.getStackKind(), append(genZeroExtend(input, to.getBitCount())));
+    }
+
+    private void genNarrow(Kind from, Kind to) {
+        T input = frameState.pop(from.getStackKind());
+        frameState.push(to.getStackKind(), append(genNarrow(input, to.getBitCount())));
+    }
+
+    private void genIncrement() {
+        int index = getStream().readLocalIndex();
+        int delta = getStream().readIncrement();
+        T x = frameState.loadLocal(index);
+        T y = appendConstant(Constant.forInt(delta));
+        frameState.storeLocal(index, append(genIntegerAdd(Kind.Int, x, y)));
+    }
+
+    private void genGoto() {
+        appendGoto(createTarget(currentBlock.getSuccessor(0), frameState));
+        assert currentBlock.numNormalSuccessors() == 1;
+    }
+
+    private void ifNode(T x, Condition cond, T y) {
+        assert !x.isDeleted() && !y.isDeleted();
+        assert currentBlock.numNormalSuccessors() == 2;
+        BciBlock trueBlock = currentBlock.getSuccessor(0);
+        BciBlock falseBlock = currentBlock.getSuccessor(1);
+        if (trueBlock == falseBlock) {
+            appendGoto(createTarget(trueBlock, frameState));
+            return;
+        }
+
+        double probability = profilingInfo.getBranchTakenProbability(bci());
+        if (probability < 0) {
+            assert probability == -1 : "invalid probability";
+            Debug.log("missing probability in %s at bci %d", method, bci());
+            probability = 0.5;
+        }
+
+        if (!optimisticOpts.removeNeverExecutedCode()) {
+            if (probability == 0) {
+                probability = 0.0000001;
+            } else if (probability == 1) {
+                probability = 0.999999;
+            }
+        }
+
+        // the mirroring and negation operations get the condition into canonical form
+        boolean mirror = cond.canonicalMirror();
+        boolean negate = cond.canonicalNegate();
+
+        T a = mirror ? y : x;
+        T b = mirror ? x : y;
+
+        CompareNode condition;
+        assert !a.getKind().isNumericFloat();
+        if (cond == Condition.EQ || cond == Condition.NE) {
+            if (a.getKind() == Kind.Object) {
+                condition = new ObjectEqualsNode(a, b);
+            } else {
+                condition = new IntegerEqualsNode(a, b);
+            }
+        } else {
+            assert a.getKind() != Kind.Object && !cond.isUnsigned();
+            condition = new IntegerLessThanNode(a, b);
+        }
+        condition = currentGraph.unique(condition);
+
+        AbstractBeginNode trueSuccessor = createBlockTarget(probability, trueBlock, frameState);
+        AbstractBeginNode falseSuccessor = createBlockTarget(1 - probability, falseBlock, frameState);
+
+        IfNode ifNode = negate ? new IfNode(condition, falseSuccessor, trueSuccessor, 1 - probability) : new IfNode(condition, trueSuccessor, falseSuccessor, probability);
+        append(ifNode);
+    }
+
+    private void genIfZero(Condition cond) {
+        T y = appendConstant(Constant.INT_0);
+        T x = frameState.ipop();
+        ifNode(x, cond, y);
+    }
+
+    private void genIfNull(Condition cond) {
+        T y = appendConstant(Constant.NULL_OBJECT);
+        T x = frameState.apop();
+        ifNode(x, cond, y);
+    }
+
+    private void genIfSame(Kind kind, Condition cond) {
+        T y = frameState.pop(kind);
+        T x = frameState.pop(kind);
+        assert !x.isDeleted() && !y.isDeleted();
+        ifNode(x, cond, y);
+    }
+
+    private void genThrow() {
+        T exception = frameState.apop();
+        append(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
+        lastInstr.setNext(handleException(exception, bci()));
+    }
+
+    private JavaType lookupType(int cpi, int bytecode) {
+        eagerResolvingForSnippets(cpi, bytecode);
+        JavaType result = constantPool.lookupType(cpi, bytecode);
+        assert !graphBuilderConfig.unresolvedIsError() || result instanceof ResolvedJavaType;
+        return result;
+    }
+
+    private JavaMethod lookupMethod(int cpi, int opcode) {
+        eagerResolvingForSnippets(cpi, opcode);
+        JavaMethod result = constantPool.lookupMethod(cpi, opcode);
+        /*
+         * In general, one cannot assume that the declaring class being initialized is useful, since
+         * the actual concrete receiver may be a different class (except for static calls). Also,
+         * interfaces are initialized only under special circumstances, so that this assertion would
+         * often fail for interface calls.
+         */
+        assert !graphBuilderConfig.unresolvedIsError() || (result instanceof ResolvedJavaMethod && (opcode != INVOKESTATIC || ((ResolvedJavaMethod) result).getDeclaringClass().isInitialized())) : result;
+        return result;
+    }
+
+    private JavaField lookupField(int cpi, int opcode) {
+        eagerResolvingForSnippets(cpi, opcode);
+        JavaField result = constantPool.lookupField(cpi, opcode);
+        assert !graphBuilderConfig.unresolvedIsError() || (result instanceof ResolvedJavaField && ((ResolvedJavaField) result).getDeclaringClass().isInitialized()) : result;
+        return result;
+    }
+
+    private Object lookupConstant(int cpi, int opcode) {
+        eagerResolvingForSnippets(cpi, opcode);
+        Object result = constantPool.lookupConstant(cpi);
+        assert !graphBuilderConfig.eagerResolving() || !(result instanceof JavaType) || (result instanceof ResolvedJavaType) : result;
+        return result;
+    }
+
+    private void eagerResolvingForSnippets(int cpi, int bytecode) {
+        if (graphBuilderConfig.eagerResolving()) {
+            constantPool.loadReferencedType(cpi, bytecode);
+        }
+    }
+
+    private JavaTypeProfile getProfileForTypeCheck(ResolvedJavaType type) {
+        if (!optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) {
+            return null;
+        } else {
+            return profilingInfo.getTypeProfile(bci());
+        }
+    }
+
+    private void genCheckCast() {
+        int cpi = getStream().readCPI();
+        JavaType type = lookupType(cpi, CHECKCAST);
+        T object = frameState.apop();
+        if (type instanceof ResolvedJavaType) {
+            JavaTypeProfile profileForTypeCheck = getProfileForTypeCheck((ResolvedJavaType) type);
+            CheckCastNode checkCastNode = append(new CheckCastNode((ResolvedJavaType) type, object, profileForTypeCheck, false));
+            frameState.apush(checkCastNode);
+        } else {
+            handleUnresolvedCheckCast(type, object);
+        }
+    }
+
+    private void genInstanceOf() {
+        int cpi = getStream().readCPI();
+        JavaType type = lookupType(cpi, INSTANCEOF);
+        T object = frameState.apop();
+        if (type instanceof ResolvedJavaType) {
+            ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+            InstanceOfNode instanceOfNode = new InstanceOfNode((ResolvedJavaType) type, object, getProfileForTypeCheck(resolvedType));
+            frameState.ipush(append(new ConditionalNode(currentGraph.unique(instanceOfNode))));
+        } else {
+            handleUnresolvedInstanceOf(type, object);
+        }
+    }
+
+    void genNewInstance(int cpi) {
+        JavaType type = lookupType(cpi, NEW);
+        if (type instanceof ResolvedJavaType && ((ResolvedJavaType) type).isInitialized()) {
+            frameState.apush(append(createNewInstance((ResolvedJavaType) type, true)));
+        } else {
+            handleUnresolvedNewInstance(type);
+        }
+    }
+
+    protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) {
+        return new NewInstanceNode(type, fillContents);
+    }
+
+    /**
+     * Gets the kind of array elements for the array type code that appears in a
+     * {@link Bytecodes#NEWARRAY} bytecode.
+     * 
+     * @param code the array type code
+     * @return the kind from the array type code
+     */
+    public static Class<?> arrayTypeCodeToClass(int code) {
+        // Checkstyle: stop
+        switch (code) {
+            case 4:
+                return boolean.class;
+            case 5:
+                return char.class;
+            case 6:
+                return float.class;
+            case 7:
+                return double.class;
+            case 8:
+                return byte.class;
+            case 9:
+                return short.class;
+            case 10:
+                return int.class;
+            case 11:
+                return long.class;
+            default:
+                throw new IllegalArgumentException("unknown array type code: " + code);
+        }
+        // Checkstyle: resume
+    }
+
+    private void genNewPrimitiveArray(int typeCode) {
+        Class<?> clazz = arrayTypeCodeToClass(typeCode);
+        ResolvedJavaType elementType = metaAccess.lookupJavaType(clazz);
+        frameState.apush(append(createNewArray(elementType, frameState.ipop(), true)));
+    }
+
+    private void genNewObjectArray(int cpi) {
+        JavaType type = lookupType(cpi, ANEWARRAY);
+        T length = frameState.ipop();
+        if (type instanceof ResolvedJavaType) {
+            frameState.apush(append(createNewArray((ResolvedJavaType) type, length, true)));
+        } else {
+            handleUnresolvedNewObjectArray(type, length);
+        }
+
+    }
+
+    protected NewArrayNode createNewArray(ResolvedJavaType elementType, T length, boolean fillContents) {
+        return new NewArrayNode(elementType, length, fillContents);
+    }
+
+    private void genNewMultiArray(int cpi) {
+        JavaType type = lookupType(cpi, MULTIANEWARRAY);
+        int rank = getStream().readUByte(bci() + 3);
+        T[] dims = new T[rank];
+        for (int i = rank - 1; i >= 0; i--) {
+            dims[i] = frameState.ipop();
+        }
+        if (type instanceof ResolvedJavaType) {
+            frameState.apush(append(createNewMultiArray((ResolvedJavaType) type, dims)));
+        } else {
+            handleUnresolvedNewMultiArray(type, dims);
+        }
+    }
+
+    protected NewMultiArrayNode createNewMultiArray(ResolvedJavaType type, T[] dimensions) {
+        return new NewMultiArrayNode(type, dimensions);
+    }
+
+    private void genGetField(JavaField field) {
+        emitExplicitExceptions(frameState.peek(0), null);
+
+        Kind kind = field.getKind();
+        T receiver = frameState.apop();
+        if ((field instanceof ResolvedJavaField) && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
+            appendOptimizedLoadField(kind, new LoadFieldNode(receiver, (ResolvedJavaField) field));
+        } else {
+            handleUnresolvedLoadField(field, receiver);
+        }
+    }
+
+    public static class ExceptionInfo {
+
+        public final FixedWithNextNode exceptionEdge;
+        public final T exception;
+
+        public ExceptionInfo(FixedWithNextNode exceptionEdge, T exception) {
+            this.exceptionEdge = exceptionEdge;
+            this.exception = exception;
+        }
+    }
+
+    private void emitNullCheck(T receiver) {
+        if (ObjectStamp.isObjectNonNull(receiver.stamp())) {
+            return;
+        }
+        BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode(this));
+        BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode(this));
+        append(new IfNode(currentGraph.unique(new IsNullNode(receiver)), trueSucc, falseSucc, 0.01));
+        lastInstr = falseSucc;
+
+        if (OmitHotExceptionStacktrace.getValue()) {
+            T exception = ConstantNode.forObject(cachedNullPointerException, metaAccess, currentGraph);
+            trueSucc.setNext(handleException(exception, bci()));
+        } else {
+            DeferredForeignCallNode call = currentGraph.add(new DeferredForeignCallNode(CREATE_NULL_POINTER_EXCEPTION));
+            call.setStamp(StampFactory.exactNonNull(metaAccess.lookupJavaType(CREATE_NULL_POINTER_EXCEPTION.getResultType())));
+            call.setStateAfter(frameState.create(bci()));
+            trueSucc.setNext(call);
+            call.setNext(handleException(call, bci()));
+        }
+    }
+
+    private static final ArrayIndexOutOfBoundsException cachedArrayIndexOutOfBoundsException = new ArrayIndexOutOfBoundsException();
+    private static final NullPointerException cachedNullPointerException = new NullPointerException();
+    static {
+        cachedArrayIndexOutOfBoundsException.setStackTrace(new StackTraceElement[0]);
+        cachedNullPointerException.setStackTrace(new StackTraceElement[0]);
+    }
+
+    private void emitBoundsCheck(T index, T length) {
+        BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode(this));
+        BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode(this));
+        append(new IfNode(currentGraph.unique(new IntegerBelowThanNode(index, length)), trueSucc, falseSucc, 0.99));
+        lastInstr = trueSucc;
+
+        if (OmitHotExceptionStacktrace.getValue()) {
+            T exception = ConstantNode.forObject(cachedArrayIndexOutOfBoundsException, metaAccess, currentGraph);
+            falseSucc.setNext(handleException(exception, bci()));
+        } else {
+            DeferredForeignCallNode call = currentGraph.add(new DeferredForeignCallNode(CREATE_OUT_OF_BOUNDS_EXCEPTION, index));
+            call.setStamp(StampFactory.exactNonNull(metaAccess.lookupJavaType(CREATE_OUT_OF_BOUNDS_EXCEPTION.getResultType())));
+            call.setStateAfter(frameState.create(bci()));
+            falseSucc.setNext(call);
+            call.setNext(handleException(call, bci()));
+        }
+    }
+
+    private static final DebugMetric EXPLICIT_EXCEPTIONS = Debug.metric("ExplicitExceptions");
+
+    protected void emitExplicitExceptions(T receiver, T outOfBoundsIndex) {
+        assert receiver != null;
+        if (graphBuilderConfig.omitAllExceptionEdges() || (optimisticOpts.useExceptionProbabilityForOperations() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE)) {
+            return;
+        }
+
+        emitNullCheck(receiver);
+        if (outOfBoundsIndex != null) {
+            T length = append(new ArrayLengthNode(receiver));
+            emitBoundsCheck(outOfBoundsIndex, length);
+        }
+        EXPLICIT_EXCEPTIONS.increment();
+    }
+
+    private void genPutField(JavaField field) {
+        emitExplicitExceptions(frameState.peek(1), null);
+
+        T value = frameState.pop(field.getKind().getStackKind());
+        T receiver = frameState.apop();
+        if (field instanceof ResolvedJavaField && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
+            appendOptimizedStoreField(new StoreFieldNode(receiver, (ResolvedJavaField) field, value));
+        } else {
+            handleUnresolvedStoreField(field, value, receiver);
+        }
+    }
+
+    private void genGetStatic(JavaField field) {
+        Kind kind = field.getKind();
+        if (field instanceof ResolvedJavaField && ((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
+            appendOptimizedLoadField(kind, new LoadFieldNode(null, (ResolvedJavaField) field));
+        } else {
+            handleUnresolvedLoadField(field, null);
+        }
+    }
+
+    private void genPutStatic(JavaField field) {
+        T value = frameState.pop(field.getKind().getStackKind());
+        if (field instanceof ResolvedJavaField && ((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
+            appendOptimizedStoreField(new StoreFieldNode(null, (ResolvedJavaField) field, value));
+        } else {
+            handleUnresolvedStoreField(field, value, null);
+        }
+    }
+
+    private void appendOptimizedStoreField(StoreFieldNode store) {
+        append(store);
+    }
+
+    private void appendOptimizedLoadField(Kind kind, LoadFieldNode load) {
+        // append the load to the instruction
+        T optimized = append(load);
+        frameState.push(kind.getStackKind(), optimized);
+    }
+
+    private void genInvokeStatic(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
+            ResolvedJavaType holder = resolvedTarget.getDeclaringClass();
+            if (!holder.isInitialized() && ResolveClassBeforeStaticInvoke.getValue()) {
+                handleUnresolvedInvoke(target, InvokeKind.Static);
+            } else {
+                T[] args = frameState.popArguments(resolvedTarget.getSignature().getParameterSlots(false), resolvedTarget.getSignature().getParameterCount(false));
+                appendInvoke(InvokeKind.Static, resolvedTarget, args);
+            }
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Static);
+        }
+    }
+
+    private void genInvokeInterface(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            T[] args = frameState.popArguments(target.getSignature().getParameterSlots(true), target.getSignature().getParameterCount(true));
+            genInvokeIndirect(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Interface);
+        }
+    }
+
+    private void genInvokeDynamic(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            Object appendix = constantPool.lookupAppendix(stream.readCPI4(), Bytecodes.INVOKEDYNAMIC);
+            if (appendix != null) {
+                frameState.apush(ConstantNode.forObject(appendix, metaAccess, currentGraph));
+            }
+            T[] args = frameState.popArguments(target.getSignature().getParameterSlots(false), target.getSignature().getParameterCount(false));
+            appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Static);
+        }
+    }
+
+    private void genInvokeVirtual(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            /*
+             * Special handling for runtimes that rewrite an invocation of MethodHandle.invoke(...)
+             * or MethodHandle.invokeExact(...) to a static adapter. HotSpot does this - see
+             * https://wikis.oracle.com/display/HotSpotInternals/Method+handles +and+invokedynamic
+             */
+            boolean hasReceiver = !isStatic(((ResolvedJavaMethod) target).getModifiers());
+            Object appendix = constantPool.lookupAppendix(stream.readCPI(), Bytecodes.INVOKEVIRTUAL);
+            if (appendix != null) {
+                frameState.apush(ConstantNode.forObject(appendix, metaAccess, currentGraph));
+            }
+            T[] args = frameState.popArguments(target.getSignature().getParameterSlots(hasReceiver), target.getSignature().getParameterCount(hasReceiver));
+            if (hasReceiver) {
+                genInvokeIndirect(InvokeKind.Virtual, (ResolvedJavaMethod) target, args);
+            } else {
+                appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
+            }
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Virtual);
+        }
+
+    }
+
+    private void genInvokeSpecial(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            assert target != null;
+            assert target.getSignature() != null;
+            T[] args = frameState.popArguments(target.getSignature().getParameterSlots(true), target.getSignature().getParameterCount(true));
+            invokeDirect((ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Special);
+        }
+    }
+
+    private void genInvokeIndirect(InvokeKind invokeKind, ResolvedJavaMethod target, T[] args) {
+        T receiver = args[0];
+        // attempt to devirtualize the call
+        ResolvedJavaType klass = target.getDeclaringClass();
+
+        // 0. check for trivial cases
+        if (target.canBeStaticallyBound()) {
+            // check for trivial cases (e.g. final methods, nonvirtual methods)
+            invokeDirect(target, args);
+            return;
+        }
+        // 1. check if the exact type of the receiver can be determined
+        ResolvedJavaType exact = klass.asExactType();
+        if (exact == null && receiver.stamp() instanceof ObjectStamp) {
+            ObjectStamp receiverStamp = (ObjectStamp) receiver.stamp();
+            if (receiverStamp.isExactType()) {
+                exact = receiverStamp.type();
+            }
+        }
+        if (exact != null) {
+            // either the holder class is exact, or the receiver object has an exact type
+            ResolvedJavaMethod exactMethod = exact.resolveMethod(target);
+            if (exactMethod != null) {
+                invokeDirect(exactMethod, args);
+                return;
+            }
+        }
+        // devirtualization failed, produce an actual invokevirtual
+        appendInvoke(invokeKind, target, args);
+    }
+
+    private void invokeDirect(ResolvedJavaMethod target, T[] args) {
+        appendInvoke(InvokeKind.Special, target, args);
+    }
+
+    private void appendInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, T[] args) {
+        Kind resultType = targetMethod.getSignature().getReturnKind();
+        if (DeoptALot.getValue()) {
+            append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
+            frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, currentGraph));
+            return;
+        }
+
+        JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass());
+        if (graphBuilderConfig.eagerResolving()) {
+            returnType = returnType.resolve(targetMethod.getDeclaringClass());
+        }
+        if (invokeKind != InvokeKind.Static) {
+            emitExplicitExceptions(args[0], null);
+            if (invokeKind != InvokeKind.Special && this.optimisticOpts.useTypeCheckHints()) {
+                JavaTypeProfile profile = profilingInfo.getTypeProfile(bci());
+                args[0] = TypeProfileProxyNode.create(args[0], profile);
+            }
+        }
+        MethodCallTargetNode callTarget = currentGraph.add(createMethodCallTarget(invokeKind, targetMethod, args, returnType));
+
+        // be conservative if information was not recorded (could result in endless recompiles
+        // otherwise)
+        if (graphBuilderConfig.omitAllExceptionEdges() || (optimisticOpts.useExceptionProbability() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE)) {
+            createInvoke(callTarget, resultType);
+        } else {
+            assert bci() == currentBlock.endBci;
+            frameState.clearNonLiveLocals(currentBlock, liveness, false);
+
+            InvokeWithExceptionNode invoke = createInvokeWithException(callTarget, resultType);
+
+            BciBlock nextBlock = currentBlock.getSuccessor(0);
+            invoke.setNext(createTarget(nextBlock, frameState));
+        }
+    }
+
+    protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, T[] args, JavaType returnType) {
+        return new MethodCallTargetNode(invokeKind, targetMethod, args, returnType);
+    }
+
+    protected InvokeNode createInvoke(CallTargetNode callTarget, Kind resultType) {
+        InvokeNode invoke = append(new InvokeNode(callTarget, bci()));
+        frameState.pushReturn(resultType, invoke);
+        return invoke;
+    }
+
+    protected InvokeWithExceptionNode createInvokeWithException(CallTargetNode callTarget, Kind resultType) {
+        DispatchBeginNode exceptionEdge = handleException(null, bci());
+        InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
+        frameState.pushReturn(resultType, invoke);
+        BciBlock nextBlock = currentBlock.getSuccessor(0);
+        invoke.setStateAfter(frameState.create(nextBlock.startBci));
+        return invoke;
+    }
+
+    private void genReturn(T x) {
+        frameState.setRethrowException(false);
+        frameState.clearStack();
+        if (graphBuilderConfig.eagerInfopointMode()) {
+            append(new InfopointNode(InfopointReason.METHOD_END, frameState.create(bci())));
+        }
+
+        synchronizedEpilogue(FrameState.AFTER_BCI, x);
+        if (frameState.lockDepth() != 0) {
+            throw new BailoutException("unbalanced monitors");
+        }
+
+        append(new ReturnNode(x));
+    }
+
+    private MonitorEnterNode genMonitorEnter(T x) {
+        MonitorIdNode monitorId = currentGraph.add(new MonitorIdNode(frameState.lockDepth()));
+        MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, monitorId));
+        frameState.pushLock(x, monitorId);
+        return monitorEnter;
+    }
+
+    private MonitorExitNode genMonitorExit(T x, T returnValue) {
+        MonitorIdNode monitorId = frameState.peekMonitorId();
+        T lockedObject = frameState.popLock();
+        if (GraphUtil.originalValue(lockedObject) != GraphUtil.originalValue(x)) {
+            throw new BailoutException("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject));
+        }
+        MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, returnValue));
+        return monitorExit;
+    }
+
+    private void genJsr(int dest) {
+        BciBlock successor = currentBlock.jsrSuccessor;
+        assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci();
+        JsrScope scope = currentBlock.jsrScope;
+        if (!successor.jsrScope.pop().equals(scope)) {
+            throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
+        }
+        if (successor.jsrScope.nextReturnAddress() != stream().nextBCI()) {
+            throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
+        }
+        frameState.push(Kind.Int, ConstantNode.forInt(stream().nextBCI(), currentGraph));
+        appendGoto(createTarget(successor, frameState));
+    }
+
+    private void genRet(int localIndex) {
+        BciBlock successor = currentBlock.retSuccessor;
+        T local = frameState.loadLocal(localIndex);
+        JsrScope scope = currentBlock.jsrScope;
+        int retAddress = scope.nextReturnAddress();
+        append(new FixedGuardNode(currentGraph.unique(new IntegerEqualsNode(local, ConstantNode.forInt(retAddress, currentGraph))), JavaSubroutineMismatch, InvalidateReprofile));
+        if (!successor.jsrScope.equals(scope.pop())) {
+            throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
+        }
+        appendGoto(createTarget(successor, frameState));
+    }
+
+    private double[] switchProbability(int numberOfCases, int bci) {
+        double[] prob = profilingInfo.getSwitchProbabilities(bci);
+        if (prob != null) {
+            assert prob.length == numberOfCases;
+        } else {
+            Debug.log("Missing probability (switch) in %s at bci %d", method, bci);
+            prob = new double[numberOfCases];
+            for (int i = 0; i < numberOfCases; i++) {
+                prob[i] = 1.0d / numberOfCases;
+            }
+        }
+        assert allPositive(prob);
+        return prob;
+    }
+
+    private static boolean allPositive(double[] a) {
+        for (double d : a) {
+            if (d < 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Helper function that sums up the probabilities of all keys that lead to a specific successor.
+     * 
+     * @return an array of size successorCount with the accumulated probability for each successor.
+     */
+    private static double[] successorProbabilites(int successorCount, int[] keySuccessors, double[] keyProbabilities) {
+        double[] probability = new double[successorCount];
+        for (int i = 0; i < keySuccessors.length; i++) {
+            probability[keySuccessors[i]] += keyProbabilities[i];
+        }
+        return probability;
+    }
+
+    private void genSwitch(BytecodeSwitch bs) {
+        int bci = bci();
+        T value = frameState.ipop();
+
+        int nofCases = bs.numberOfCases();
+        double[] keyProbabilities = switchProbability(nofCases + 1, bci);
+
+        Map<Integer, SuccessorInfo> bciToBlockSuccessorIndex = new HashMap<>();
+        for (int i = 0; i < currentBlock.getSuccessorCount(); i++) {
+            assert !bciToBlockSuccessorIndex.containsKey(currentBlock.getSuccessor(i).startBci);
+            if (!bciToBlockSuccessorIndex.containsKey(currentBlock.getSuccessor(i).startBci)) {
+                bciToBlockSuccessorIndex.put(currentBlock.getSuccessor(i).startBci, new SuccessorInfo(i));
+            }
+        }
+
+        ArrayList<BciBlock> actualSuccessors = new ArrayList<>();
+        int[] keys = new int[nofCases];
+        int[] keySuccessors = new int[nofCases + 1];
+        int deoptSuccessorIndex = -1;
+        int nextSuccessorIndex = 0;
+        for (int i = 0; i < nofCases + 1; i++) {
+            if (i < nofCases) {
+                keys[i] = bs.keyAt(i);
+            }
+
+            if (isNeverExecutedCode(keyProbabilities[i])) {
+                if (deoptSuccessorIndex < 0) {
+                    deoptSuccessorIndex = nextSuccessorIndex++;
+                    actualSuccessors.add(null);
+                }
+                keySuccessors[i] = deoptSuccessorIndex;
+            } else {
+                int targetBci = i >= nofCases ? bs.defaultTarget() : bs.targetAt(i);
+                SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci);
+                if (info.actualIndex < 0) {
+                    info.actualIndex = nextSuccessorIndex++;
+                    actualSuccessors.add(currentBlock.getSuccessor(info.blockIndex));
+                }
+                keySuccessors[i] = info.actualIndex;
+            }
+        }
+
+        double[] successorProbabilities = successorProbabilites(actualSuccessors.size(), keySuccessors, keyProbabilities);
+        IntegerSwitchNode switchNode = append(new IntegerSwitchNode(value, actualSuccessors.size(), keys, keyProbabilities, keySuccessors));
+        for (int i = 0; i < actualSuccessors.size(); i++) {
+            switchNode.setBlockSuccessor(i, createBlockTarget(successorProbabilities[i], actualSuccessors.get(i), frameState));
+        }
+
+    }
+
+    private static class SuccessorInfo {
+
+        int blockIndex;
+        int actualIndex;
+
+        public SuccessorInfo(int blockSuccessorIndex) {
+            this.blockIndex = blockSuccessorIndex;
+            actualIndex = -1;
+        }
+    }
+
+    protected abstract T appendConstant(Constant constant);
+
+    protected abstract T append(T v);
+
+    private static class Target {
+
+        FixedNode fixed;
+        HIRFrameStateBuilder state;
+
+        public Target(FixedNode fixed, HIRFrameStateBuilder state) {
+            this.fixed = fixed;
+            this.state = state;
+        }
+    }
+
+    private Target checkLoopExit(FixedNode target, BciBlock targetBlock, HIRFrameStateBuilder state) {
+        if (currentBlock != null) {
+            long exits = currentBlock.loops & ~targetBlock.loops;
+            if (exits != 0) {
+                LoopExitNode firstLoopExit = null;
+                LoopExitNode lastLoopExit = null;
+
+                int pos = 0;
+                ArrayList<BciBlock> exitLoops = new ArrayList<>(Long.bitCount(exits));
+                do {
+                    long lMask = 1L << pos;
+                    if ((exits & lMask) != 0) {
+                        exitLoops.add(loopHeaders[pos]);
+                        exits &= ~lMask;
+                    }
+                    pos++;
+                } while (exits != 0);
+
+                Collections.sort(exitLoops, new Comparator<BciBlock>() {
+
+                    @Override
+                    public int compare(BciBlock o1, BciBlock o2) {
+                        return Long.bitCount(o2.loops) - Long.bitCount(o1.loops);
+                    }
+                });
+
+                int bci = targetBlock.startBci;
+                if (targetBlock instanceof ExceptionDispatchBlock) {
+                    bci = ((ExceptionDispatchBlock) targetBlock).deoptBci;
+                }
+                HIRFrameStateBuilder newState = state.copy();
+                for (BciBlock loop : exitLoops) {
+                    LoopBeginNode loopBegin = (LoopBeginNode) loop.firstInstruction;
+                    LoopExitNode loopExit = currentGraph.add(new LoopExitNode(loopBegin));
+                    if (lastLoopExit != null) {
+                        lastLoopExit.setNext(loopExit);
+                    }
+                    if (firstLoopExit == null) {
+                        firstLoopExit = loopExit;
+                    }
+                    lastLoopExit = loopExit;
+                    Debug.log("Target %s (%s) Exits %s, scanning framestates...", targetBlock, target, loop);
+                    newState.insertLoopProxies(loopExit, loop.entryState);
+                    loopExit.setStateAfter(newState.create(bci));
+                }
+
+                lastLoopExit.setNext(target);
+                return new Target(firstLoopExit, newState);
+            }
+        }
+        return new Target(target, state);
+    }
+
+    private FixedNode createTarget(double probability, BciBlock block, HIRFrameStateBuilder stateAfter) {
+        assert probability >= 0 && probability <= 1.01 : probability;
+        if (isNeverExecutedCode(probability)) {
+            return currentGraph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+        } else {
+            assert block != null;
+            return createTarget(block, stateAfter);
+        }
+    }
+
+    private boolean isNeverExecutedCode(double probability) {
+        return probability == 0 && optimisticOpts.removeNeverExecutedCode() && entryBCI == StructuredGraph.INVOCATION_ENTRY_BCI;
+    }
+
+    private abstract T createTarget(BciBlock block, HIRFrameStateBuilder state) {
+        assert block != null && state != null;
+        assert !block.isExceptionEntry || state.stackSize() == 1;
+
+        if (block.firstInstruction == null) {
+            /*
+             * This is the first time we see this block as a branch target. Create and return a
+             * placeholder that later can be replaced with a MergeNode when we see this block again.
+             */
+            block.firstInstruction = currentGraph.add(new BlockPlaceholderNode(this));
+            Target target = checkLoopExit(block.firstInstruction, block, state);
+            FixedNode result = target.fixed;
+            block.entryState = target.state == state ? state.copy() : target.state;
+            block.entryState.clearNonLiveLocals(block, liveness, true);
+
+            Debug.log("createTarget %s: first visit, result: %s", block, block.firstInstruction);
+            return result;
+        }
+
+        // We already saw this block before, so we have to merge states.
+        if (!block.entryState.isCompatibleWith(state)) {
+            throw new BailoutException("stacks do not match; bytecodes would not verify");
+        }
+
+        if (block.firstInstruction instanceof LoopBeginNode) {
+            assert block.isLoopHeader && currentBlock.getId() >= block.getId() : "must be backward branch";
+            /*
+             * Backward loop edge. We need to create a special LoopEndNode and merge with the loop
+             * begin node created before.
+             */
+            LoopBeginNode loopBegin = (LoopBeginNode) block.firstInstruction;
+            Target target = checkLoopExit(currentGraph.add(new LoopEndNode(loopBegin)), block, state);
+            FixedNode result = target.fixed;
+            block.entryState.merge(loopBegin, target.state);
+
+            Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
+            return result;
+        }
+        assert currentBlock == null || currentBlock.getId() < block.getId() : "must not be backward branch";
+        assert block.firstInstruction.next() == null : "bytecodes already parsed for block";
+
+        if (block.firstInstruction instanceof BlockPlaceholderNode) {
+            /*
+             * This is the second time we see this block. Create the actual MergeNode and the End
+             * Node for the already existing edge. For simplicity, we leave the placeholder in the
+             * graph and just append the new nodes after the placeholder.
+             */
+            BlockPlaceholderNode placeholder = (BlockPlaceholderNode) block.firstInstruction;
+
+            // The EndNode for the already existing edge.
+            AbstractEndNode end = currentGraph.add(new EndNode());
+            // The MergeNode that replaces the placeholder.
+            MergeNode mergeNode = currentGraph.add(new MergeNode());
+            FixedNode next = placeholder.next();
+
+            placeholder.setNext(end);
+            mergeNode.addForwardEnd(end);
+            mergeNode.setNext(next);
+
+            block.firstInstruction = mergeNode;
+        }
+
+        MergeNode mergeNode = (MergeNode) block.firstInstruction;
+
+        // The EndNode for the newly merged edge.
+        AbstractEndNode newEnd = currentGraph.add(new EndNode());
+        Target target = checkLoopExit(newEnd, block, state);
+        FixedNode result = target.fixed;
+        block.entryState.merge(mergeNode, target.state);
+        mergeNode.addForwardEnd(newEnd);
+
+        Debug.log("createTarget %s: merging state, result: %s", block, result);
+        return result;
+    }
+
+    /**
+     * Returns a block begin node with the specified state. If the specified probability is 0, the
+     * block deoptimizes immediately.
+     */
+    private AbstractBeginNode createBlockTarget(double probability, BciBlock block, HIRFrameStateBuilder stateAfter) {
+        FixedNode target = createTarget(probability, block, stateAfter);
+        AbstractBeginNode begin = AbstractBeginNode.begin(target);
+
+        assert !(target instanceof DeoptimizeNode && begin.stateAfter() != null) : "We are not allowed to set the stateAfter of the begin node, because we have to deoptimize "
+                        + "to a bci _before_ the actual if, so that the interpreter can update the profiling information.";
+        return begin;
+    }
+
+    private T synchronizedObject(HIRFrameStateBuilder state, ResolvedJavaMethod target) {
+        if (isStatic(target.getModifiers())) {
+            return appendConstant(target.getDeclaringClass().getEncoding(Representation.JavaClass));
+        } else {
+            return state.loadLocal(0);
+        }
+    }
+
+    private void processBlock(BciBlock block) {
+        // Ignore blocks that have no predecessors by the time their bytecodes are parsed
+        if (block == null || block.firstInstruction == null) {
+            Debug.log("Ignoring block %s", block);
+            return;
+        }
+        Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, block.firstInstruction, block.isLoopHeader);
+
+        lastInstr = block.firstInstruction;
+        frameState = block.entryState;
+        parseHelper.setCurrentFrameState(frameState);
+        currentBlock = block;
+
+        frameState.cleanupDeletedPhis();
+        if (lastInstr instanceof MergeNode) {
+            int bci = block.startBci;
+            if (block instanceof ExceptionDispatchBlock) {
+                bci = ((ExceptionDispatchBlock) block).deoptBci;
+            }
+            ((MergeNode) lastInstr).setStateAfter(frameState.create(bci));
+        }
+
+        if (block == unwindBlock) {
+            frameState.setRethrowException(false);
+            createUnwind();
+        } else if (block instanceof ExceptionDispatchBlock) {
+            createExceptionDispatch((ExceptionDispatchBlock) block);
+        } else {
+            frameState.setRethrowException(false);
+            iterateBytecodesForBlock(block);
+        }
+        indent.outdent();
+    }
+
+    private void connectLoopEndToBegin() {
+        for (LoopBeginNode begin : currentGraph.getNodes(LoopBeginNode.class)) {
+            if (begin.loopEnds().isEmpty()) {
+                // @formatter:off
+                // Remove loop header without loop ends.
+                // This can happen with degenerated loops like this one:
+                // for (;;) {
+                //     try {
+                //         break;
+                //     } catch (UnresolvedException iioe) {
+                //     }
+                // }
+                // @formatter:on
+                assert begin.forwardEndCount() == 1;
+                currentGraph.reduceDegenerateLoopBegin(begin);
+            } else {
+                GraphUtil.normalizeLoopBegin(begin);
+            }
+        }
+    }
+
+    private void createUnwind() {
+        assert frameState.stackSize() == 1 : frameState;
+        T exception = frameState.apop();
+        append(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
+        synchronizedEpilogue(FrameState.AFTER_EXCEPTION_BCI, null);
+        append(new UnwindNode(exception));
+    }
+
+    private void synchronizedEpilogue(int bci, T returnValue) {
+        if (Modifier.isSynchronized(method.getModifiers())) {
+            MonitorExitNode monitorExit = genMonitorExit(methodSynchronizedObject, returnValue);
+            if (returnValue != null) {
+                frameState.push(returnValue.getKind(), returnValue);
+            }
+            monitorExit.setStateAfter(frameState.create(bci));
+            assert !frameState.rethrowException();
+        }
+    }
+
+    private void createExceptionDispatch(ExceptionDispatchBlock block) {
+        assert frameState.stackSize() == 1 : frameState;
+        if (block.handler.isCatchAll()) {
+            assert block.getSuccessorCount() == 1;
+            appendGoto(createTarget(block.getSuccessor(0), frameState));
+            return;
+        }
+
+        JavaType catchType = block.handler.getCatchType();
+        if (graphBuilderConfig.eagerResolving()) {
+            catchType = lookupType(block.handler.catchTypeCPI(), INSTANCEOF);
+        }
+        boolean initialized = (catchType instanceof ResolvedJavaType);
+        if (initialized && graphBuilderConfig.getSkippedExceptionTypes() != null) {
+            ResolvedJavaType resolvedCatchType = (ResolvedJavaType) catchType;
+            for (ResolvedJavaType skippedType : graphBuilderConfig.getSkippedExceptionTypes()) {
+                if (skippedType.isAssignableFrom(resolvedCatchType)) {
+                    BciBlock nextBlock = block.getSuccessorCount() == 1 ? unwindBlock(block.deoptBci) : block.getSuccessor(1);
+                    T exception = frameState.stackAt(0);
+                    FixedNode trueSuccessor = currentGraph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+                    FixedNode nextDispatch = createTarget(nextBlock, frameState);
+                    append(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), trueSuccessor, nextDispatch, 0));
+                    return;
+                }
+            }
+        }
+
+        if (initialized) {
+            BciBlock nextBlock = block.getSuccessorCount() == 1 ? unwindBlock(block.deoptBci) : block.getSuccessor(1);
+            T exception = frameState.stackAt(0);
+            CheckCastNode checkCast = currentGraph.add(new CheckCastNode((ResolvedJavaType) catchType, exception, null, false));
+            frameState.apop();
+            frameState.push(Kind.Object, checkCast);
+            FixedNode catchSuccessor = createTarget(block.getSuccessor(0), frameState);
+            frameState.apop();
+            frameState.push(Kind.Object, exception);
+            FixedNode nextDispatch = createTarget(nextBlock, frameState);
+            checkCast.setNext(catchSuccessor);
+            append(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
+        } else {
+            handleUnresolvedExceptionType(Representation.ObjectHub, catchType);
+        }
+    }
+
+    private void appendGoto(FixedNode target) {
+        if (lastInstr != null) {
+            lastInstr.setNext(target);
+        }
+    }
+
+    private static boolean isBlockEnd(Node n) {
+        return n instanceof ControlSplitNode || n instanceof ControlSinkNode;
+    }
+
+    private void iterateBytecodesForBlock(BciBlock block) {
+        if (block.isLoopHeader) {
+            // Create the loop header block, which later will merge the backward branches of the
+            // loop.
+            AbstractEndNode preLoopEnd = currentGraph.add(new EndNode());
+            LoopBeginNode loopBegin = currentGraph.add(new LoopBeginNode());
+            lastInstr.setNext(preLoopEnd);
+            // Add the single non-loop predecessor of the loop header.
+            loopBegin.addForwardEnd(preLoopEnd);
+            lastInstr = loopBegin;
+
+            // Create phi functions for all local variables and operand stack slots.
+            frameState.insertLoopPhis(loopBegin);
+            loopBegin.setStateAfter(frameState.create(block.startBci));
+
+            /*
+             * We have seen all forward branches. All subsequent backward branches will merge to the
+             * loop header. This ensures that the loop header has exactly one non-loop predecessor.
+             */
+            block.firstInstruction = loopBegin;
+            /*
+             * We need to preserve the frame state builder of the loop header so that we can merge
+             * values for phi functions, so make a copy of it.
+             */
+            block.entryState = frameState.copy();
+
+            Debug.log("  created loop header %s", loopBegin);
+        }
+        assert lastInstr.next() == null : "instructions already appended at block " + block;
+        Debug.log("  frameState: %s", frameState);
+
+        int endBCI = stream.endBCI();
+
+        stream.setBCI(block.startBci);
+        int bci = block.startBci;
+        BytecodesParsed.add(block.endBci - bci);
+
+        while (bci < endBCI) {
+            if (graphBuilderConfig.eagerInfopointMode() && lnt != null) {
+                currentLineNumber = lnt.getLineNumber(bci);
+                if (currentLineNumber != previousLineNumber) {
+                    append(new InfopointNode(InfopointReason.LINE_NUMBER, frameState.create(bci)));
+                    previousLineNumber = currentLineNumber;
+                }
+            }
+
+            // read the opcode
+            int opcode = stream.currentBC();
+            traceState();
+            traceInstruction(bci, opcode, bci == block.startBci);
+            if (bci == entryBCI) {
+                if (block.jsrScope != JsrScope.EMPTY_SCOPE) {
+                    throw new BailoutException("OSR into a JSR scope is not supported");
+                }
+                EntryMarkerNode x = append(new EntryMarkerNode());
+                frameState.insertProxies(x);
+                x.setStateAfter(frameState.create(bci));
+            }
+            parseHelper.processBytecode(bci, opcode);
+
+            if (lastInstr == null || isBlockEnd(lastInstr) || lastInstr.next() != null) {
+                break;
+            }
+
+            stream.next();
+            bci = stream.currentBCI();
+
+            if (bci > block.endBci) {
+                frameState.clearNonLiveLocals(currentBlock, liveness, false);
+            }
+            if (lastInstr instanceof StateSplit) {
+                if (lastInstr.getClass() == AbstractBeginNode.class) {
+                    // BeginNodes do not need a frame state
+                } else {
+                    StateSplit stateSplit = (StateSplit) lastInstr;
+                    if (stateSplit.stateAfter() == null) {
+                        stateSplit.setStateAfter(frameState.create(bci));
+                    }
+                }
+            }
+            if (bci < endBCI) {
+                if (bci > block.endBci) {
+                    assert !block.getSuccessor(0).isExceptionEntry;
+                    assert block.numNormalSuccessors() == 1;
+                    // we fell through to the next block, add a goto and break
+                    appendGoto(createTarget(block.getSuccessor(0), frameState));
+                    break;
+                }
+            }
+        }
+    }
+
+// private final int traceLevel = Options.TraceBytecodeParserLevel.getValue();
+//
+// private void traceState() {
+// if (traceLevel >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
+// Debug.log(String.format("|   state [nr locals = %d, stack depth = %d, method = %s]",
+// frameState.localsSize(), frameState.stackSize(), method));
+// for (int i = 0; i < frameState.localsSize(); ++i) {
+// T value = frameState.localAt(i);
+// Debug.log(String.format("|   local[%d] = %-8s : %s", i, value == null ? "bogus" :
+// value.getKind().getJavaName(), value));
+// }
+// for (int i = 0; i < frameState.stackSize(); ++i) {
+// T value = frameState.stackAt(i);
+// Debug.log(String.format("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" :
+// value.getKind().getJavaName(), value));
+// }
+// }
+// }
+
+    public void processBytecode(int bci, int opcode) {
+        int cpi;
+
+        // Checkstyle: stop
+        // @formatter:off
+    switch (opcode) {
+        case NOP            : /* nothing to do */ break;
+        case ACONST_NULL    : frameState.apush(appendConstant(Constant.NULL_OBJECT)); break;
+        case ICONST_M1      : frameState.ipush(appendConstant(Constant.INT_MINUS_1)); break;
+        case ICONST_0       : frameState.ipush(appendConstant(Constant.INT_0)); break;
+        case ICONST_1       : frameState.ipush(appendConstant(Constant.INT_1)); break;
+        case ICONST_2       : frameState.ipush(appendConstant(Constant.INT_2)); break;
+        case ICONST_3       : frameState.ipush(appendConstant(Constant.INT_3)); break;
+        case ICONST_4       : frameState.ipush(appendConstant(Constant.INT_4)); break;
+        case ICONST_5       : frameState.ipush(appendConstant(Constant.INT_5)); break;
+        case LCONST_0       : frameState.lpush(appendConstant(Constant.LONG_0)); break;
+        case LCONST_1       : frameState.lpush(appendConstant(Constant.LONG_1)); break;
+        case FCONST_0       : frameState.fpush(appendConstant(Constant.FLOAT_0)); break;
+        case FCONST_1       : frameState.fpush(appendConstant(Constant.FLOAT_1)); break;
+        case FCONST_2       : frameState.fpush(appendConstant(Constant.FLOAT_2)); break;
+        case DCONST_0       : frameState.dpush(appendConstant(Constant.DOUBLE_0)); break;
+        case DCONST_1       : frameState.dpush(appendConstant(Constant.DOUBLE_1)); break;
+        case BIPUSH         : frameState.ipush(appendConstant(Constant.forInt(stream.readByte()))); break;
+        case SIPUSH         : frameState.ipush(appendConstant(Constant.forInt(stream.readShort()))); break;
+        case LDC            : // fall through
+        case LDC_W          : // fall through
+        case LDC2_W         : genLoadConstant(stream.readCPI(), opcode); break;
+        case ILOAD          : loadLocal(stream.readLocalIndex(), Kind.Int); break;
+        case LLOAD          : loadLocal(stream.readLocalIndex(), Kind.Long); break;
+        case FLOAD          : loadLocal(stream.readLocalIndex(), Kind.Float); break;
+        case DLOAD          : loadLocal(stream.readLocalIndex(), Kind.Double); break;
+        case ALOAD          : loadLocal(stream.readLocalIndex(), Kind.Object); break;
+        case ILOAD_0        : // fall through
+        case ILOAD_1        : // fall through
+        case ILOAD_2        : // fall through
+        case ILOAD_3        : loadLocal(opcode - ILOAD_0, Kind.Int); break;
+        case LLOAD_0        : // fall through
+        case LLOAD_1        : // fall through
+        case LLOAD_2        : // fall through
+        case LLOAD_3        : loadLocal(opcode - LLOAD_0, Kind.Long); break;
+        case FLOAD_0        : // fall through
+        case FLOAD_1        : // fall through
+        case FLOAD_2        : // fall through
+        case FLOAD_3        : loadLocal(opcode - FLOAD_0, Kind.Float); break;
+        case DLOAD_0        : // fall through
+        case DLOAD_1        : // fall through
+        case DLOAD_2        : // fall through
+        case DLOAD_3        : loadLocal(opcode - DLOAD_0, Kind.Double); break;
+        case ALOAD_0        : // fall through
+        case ALOAD_1        : // fall through
+        case ALOAD_2        : // fall through
+        case ALOAD_3        : loadLocal(opcode - ALOAD_0, Kind.Object); break;
+        case IALOAD         : genLoadIndexed(Kind.Int   ); break;
+        case LALOAD         : genLoadIndexed(Kind.Long  ); break;
+        case FALOAD         : genLoadIndexed(Kind.Float ); break;
+        case DALOAD         : genLoadIndexed(Kind.Double); break;
+        case AALOAD         : genLoadIndexed(Kind.Object); break;
+        case BALOAD         : genLoadIndexed(Kind.Byte  ); break;
+        case CALOAD         : genLoadIndexed(Kind.Char  ); break;
+        case SALOAD         : genLoadIndexed(Kind.Short ); break;
+        case ISTORE         : storeLocal(Kind.Int, stream.readLocalIndex()); break;
+        case LSTORE         : storeLocal(Kind.Long, stream.readLocalIndex()); break;
+        case FSTORE         : storeLocal(Kind.Float, stream.readLocalIndex()); break;
+        case DSTORE         : storeLocal(Kind.Double, stream.readLocalIndex()); break;
+        case ASTORE         : storeLocal(Kind.Object, stream.readLocalIndex()); break;
+        case ISTORE_0       : // fall through
+        case ISTORE_1       : // fall through
+        case ISTORE_2       : // fall through
+        case ISTORE_3       : storeLocal(Kind.Int, opcode - ISTORE_0); break;
+        case LSTORE_0       : // fall through
+        case LSTORE_1       : // fall through
+        case LSTORE_2       : // fall through
+        case LSTORE_3       : storeLocal(Kind.Long, opcode - LSTORE_0); break;
+        case FSTORE_0       : // fall through
+        case FSTORE_1       : // fall through
+        case FSTORE_2       : // fall through
+        case FSTORE_3       : storeLocal(Kind.Float, opcode - FSTORE_0); break;
+        case DSTORE_0       : // fall through
+        case DSTORE_1       : // fall through
+        case DSTORE_2       : // fall through
+        case DSTORE_3       : storeLocal(Kind.Double, opcode - DSTORE_0); break;
+        case ASTORE_0       : // fall through
+        case ASTORE_1       : // fall through
+        case ASTORE_2       : // fall through
+        case ASTORE_3       : storeLocal(Kind.Object, opcode - ASTORE_0); break;
+        case IASTORE        : genStoreIndexed(Kind.Int   ); break;
+        case LASTORE        : genStoreIndexed(Kind.Long  ); break;
+        case FASTORE        : genStoreIndexed(Kind.Float ); break;
+        case DASTORE        : genStoreIndexed(Kind.Double); break;
+        case AASTORE        : genStoreIndexed(Kind.Object); break;
+        case BASTORE        : genStoreIndexed(Kind.Byte  ); break;
+        case CASTORE        : genStoreIndexed(Kind.Char  ); break;
+        case SASTORE        : genStoreIndexed(Kind.Short ); break;
+        case POP            : // fall through
+        case POP2           : // fall through
+        case DUP            : // fall through
+        case DUP_X1         : // fall through
+        case DUP_X2         : // fall through
+        case DUP2           : // fall through
+        case DUP2_X1        : // fall through
+        case DUP2_X2        : // fall through
+        case SWAP           : stackOp(opcode); break;
+        case IADD           : // fall through
+        case ISUB           : // fall through
+        case IMUL           : genArithmeticOp(Kind.Int, opcode); break;
+        case IDIV           : // fall through
+        case IREM           : genIntegerDivOp(Kind.Int, opcode); break;
+        case LADD           : // fall through
+        case LSUB           : // fall through
+        case LMUL           : genArithmeticOp(Kind.Long, opcode); break;
+        case LDIV           : // fall through
+        case LREM           : genIntegerDivOp(Kind.Long, opcode); break;
+        case FADD           : // fall through
+        case FSUB           : // fall through
+        case FMUL           : // fall through
+        case FDIV           : // fall through
+        case FREM           : genArithmeticOp(Kind.Float, opcode); break;
+        case DADD           : // fall through
+        case DSUB           : // fall through
+        case DMUL           : // fall through
+        case DDIV           : // fall through
+        case DREM           : genArithmeticOp(Kind.Double, opcode); break;
+        case INEG           : genNegateOp(Kind.Int); break;
+        case LNEG           : genNegateOp(Kind.Long); break;
+        case FNEG           : genNegateOp(Kind.Float); break;
+        case DNEG           : genNegateOp(Kind.Double); break;
+        case ISHL           : // fall through
+        case ISHR           : // fall through
+        case IUSHR          : genShiftOp(Kind.Int, opcode); break;
+        case IAND           : // fall through
+        case IOR            : // fall through
+        case IXOR           : genLogicOp(Kind.Int, opcode); break;
+        case LSHL           : // fall through
+        case LSHR           : // fall through
+        case LUSHR          : genShiftOp(Kind.Long, opcode); break;
+        case LAND           : // fall through
+        case LOR            : // fall through
+        case LXOR           : genLogicOp(Kind.Long, opcode); break;
+        case IINC           : genIncrement(); break;
+        case I2F            : genFloatConvert(FloatConvert.I2F, Kind.Int, Kind.Float); break;
+        case I2D            : genFloatConvert(FloatConvert.I2D, Kind.Int, Kind.Double); break;
+        case L2F            : genFloatConvert(FloatConvert.L2F, Kind.Long, Kind.Float); break;
+        case L2D            : genFloatConvert(FloatConvert.L2D, Kind.Long, Kind.Double); break;
+        case F2I            : genFloatConvert(FloatConvert.F2I, Kind.Float, Kind.Int); break;
+        case F2L            : genFloatConvert(FloatConvert.F2L, Kind.Float, Kind.Long); break;
+        case F2D            : genFloatConvert(FloatConvert.F2D, Kind.Float, Kind.Double); break;
+        case D2I            : genFloatConvert(FloatConvert.D2I, Kind.Double, Kind.Int); break;
+        case D2L            : genFloatConvert(FloatConvert.D2L, Kind.Double, Kind.Long); break;
+        case D2F            : genFloatConvert(FloatConvert.D2F, Kind.Double, Kind.Float); break;
+        case L2I            : genNarrow(Kind.Long, Kind.Int); break;
+        case I2L            : genSignExtend(Kind.Int, Kind.Long); break;
+        case I2B            : genSignExtend(Kind.Byte, Kind.Int); break;
+        case I2S            : genSignExtend(Kind.Short, Kind.Int); break;
+        case I2C            : genZeroExtend(Kind.Char, Kind.Int); break;
+        case LCMP           : genCompareOp(Kind.Long, false); break;
+        case FCMPL          : genCompareOp(Kind.Float, true); break;
+        case FCMPG          : genCompareOp(Kind.Float, false); break;
+        case DCMPL          : genCompareOp(Kind.Double, true); break;
+        case DCMPG          : genCompareOp(Kind.Double, false); break;
+        case IFEQ           : genIfZero(Condition.EQ); break;
+        case IFNE           : genIfZero(Condition.NE); break;
+        case IFLT           : genIfZero(Condition.LT); break;
+        case IFGE           : genIfZero(Condition.GE); break;
+        case IFGT           : genIfZero(Condition.GT); break;
+        case IFLE           : genIfZero(Condition.LE); break;
+        case IF_ICMPEQ      : genIfSame(Kind.Int, Condition.EQ); break;
+        case IF_ICMPNE      : genIfSame(Kind.Int, Condition.NE); break;
+        case IF_ICMPLT      : genIfSame(Kind.Int, Condition.LT); break;
+        case IF_ICMPGE      : genIfSame(Kind.Int, Condition.GE); break;
+        case IF_ICMPGT      : genIfSame(Kind.Int, Condition.GT); break;
+        case IF_ICMPLE      : genIfSame(Kind.Int, Condition.LE); break;
+        case IF_ACMPEQ      : genIfSame(Kind.Object, Condition.EQ); break;
+        case IF_ACMPNE      : genIfSame(Kind.Object, Condition.NE); break;
+        case GOTO           : genGoto(); break;
+        case JSR            : genJsr(stream.readBranchDest()); break;
+        case RET            : genRet(stream.readLocalIndex()); break;
+        case TABLESWITCH    : genSwitch(new BytecodeTableSwitch(getStream(), bci())); break;
+        case LOOKUPSWITCH   : genSwitch(new BytecodeLookupSwitch(stream(), bci())); break;
+        case IRETURN        : genReturn(frameState.ipop()); break;
+        case LRETURN        : genReturn(frameState.lpop()); break;
+        case FRETURN        : genReturn(frameState.fpop()); break;
+        case DRETURN        : genReturn(frameState.dpop()); break;
+        case ARETURN        : genReturn(frameState.apop()); break;
+        case RETURN         : genReturn(null); break;
+        case GETSTATIC      : cpi = stream.readCPI(); genGetStatic(lookupField(cpi, opcode)); break;
+        case PUTSTATIC      : cpi = stream.readCPI(); genPutStatic(lookupField(cpi, opcode)); break;
+        case GETFIELD       : cpi = stream.readCPI(); genGetField(lookupField(cpi, opcode)); break;
+        case PUTFIELD       : cpi = stream.readCPI(); genPutField(lookupField(cpi, opcode)); break;
+        case INVOKEVIRTUAL  : cpi = stream.readCPI(); genInvokeVirtual(lookupMethod(cpi, opcode)); break;
+        case INVOKESPECIAL  : cpi = stream.readCPI(); genInvokeSpecial(lookupMethod(cpi, opcode)); break;
+        case INVOKESTATIC   : cpi = stream.readCPI(); genInvokeStatic(lookupMethod(cpi, opcode)); break;
+        case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(lookupMethod(cpi, opcode)); break;
+        case INVOKEDYNAMIC  : cpi = stream.readCPI4(); genInvokeDynamic(lookupMethod(cpi, opcode)); break;
+        case NEW            : genNewInstance(stream.readCPI()); break;
+        case NEWARRAY       : genNewPrimitiveArray(stream.readLocalIndex()); break;
+        case ANEWARRAY      : genNewObjectArray(stream.readCPI()); break;
+        case ARRAYLENGTH    : genArrayLength(); break;
+        case ATHROW         : genThrow(); break;
+        case CHECKCAST      : genCheckCast(); break;
+        case INSTANCEOF     : genInstanceOf(); break;
+        case MONITORENTER   : genMonitorEnter(frameState.apop()); break;
+        case MONITOREXIT    : genMonitorExit(frameState.apop(), null); break;
+        case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break;
+        case IFNULL         : genIfNull(Condition.EQ); break;
+        case IFNONNULL      : genIfNull(Condition.NE); break;
+        case GOTO_W         : genGoto(); break;
+        case JSR_W          : genJsr(stream.readBranchDest()); break;
+        case BREAKPOINT:
+            throw new BailoutException("concurrent setting of breakpoint");
+        default:
+            throw new BailoutException("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]");
+    }
+    // @formatter:on
+        // Checkstyle: resume
+    }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Mon Mar 24 13:56:56 2014 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Mon Mar 24 16:16:31 2014 +0100
@@ -98,6 +98,168 @@
 
     public static class Instance extends Phase {
 
+        private class BytecodeParseHelperValueNode extends BytecodeParseHelper<ValueNode> {
+
+            @Override
+            protected void handleUnresolvedLoadConstant(JavaType type) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                frameState.push(Kind.Object, appendConstant(Constant.NULL_OBJECT));
+            }
+
+            @Override
+            protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new FixedGuardNode(currentGraph.unique(new IsNullNode(object)), Unresolved, InvalidateRecompile));
+                frameState.apush(appendConstant(Constant.NULL_OBJECT));
+            }
+
+            @Override
+            protected void handleUnresolvedInstanceOf(JavaType type, ValueNode object) {
+                assert !graphBuilderConfig.eagerResolving();
+                BlockPlaceholderNode successor = currentGraph.add(new BlockPlaceholderNode(this));
+                DeoptimizeNode deopt = currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                append(new IfNode(currentGraph.unique(new IsNullNode(object)), successor, deopt, 1));
+                lastInstr = successor;
+                frameState.ipush(appendConstant(Constant.INT_0));
+
+            }
+
+            @Override
+            protected void handleUnresolvedNewInstance(JavaType type) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                frameState.apush(appendConstant(Constant.NULL_OBJECT));
+            }
+
+            @Override
+            protected void handleUnresolvedNewObjectArray(JavaType type, ValueNode length) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                frameState.apush(appendConstant(Constant.NULL_OBJECT));
+            }
+
+            @Override
+            protected void handleUnresolvedNewMultiArray(JavaType type, ValueNode[] dims) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                frameState.apush(appendConstant(Constant.NULL_OBJECT));
+
+            }
+
+            @Override
+            protected void handleUnresolvedLoadField(JavaField field, ValueNode receiver) {
+                assert !graphBuilderConfig.eagerResolving();
+                Kind kind = field.getKind();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+                frameState.push(kind.getStackKind(), appendConstant(Constant.defaultForKind(kind)));
+            }
+
+            @Override
+            protected void handleUnresolvedStoreField(JavaField field, ValueNode value, ValueNode receiver) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+            }
+
+            @Override
+            protected void handleUnresolvedExceptionType(Representation representation, JavaType type) {
+                assert !graphBuilderConfig.eagerResolving();
+                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+            }
+
+            @Override
+            protected ValueNode genLoadIndexed(ValueNode index, ValueNode array, Kind kind) {
+                return new LoadIndexedNode(array, index, kind);
+            }
+
+            @Override
+            protected ValueNode genStoreIndexed(ValueNode array, ValueNode index, Kind kind, ValueNode value) {
+                return new StoreIndexedNode(array, index, kind, value);
+            }
+
+            @Override
+            protected ValueNode genIntegerAdd(Kind kind, ValueNode x, ValueNode y) {
+                return new IntegerAddNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genIntegerSub(Kind kind, ValueNode x, ValueNode y) {
+                return new IntegerSubNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genIntegerMul(Kind kind, ValueNode x, ValueNode y) {
+                return new IntegerMulNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genFloatAdd(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
+                return new FloatAddNode(StampFactory.forKind(kind), x, y, isStrictFP);
+            }
+
+            @Override
+            protected ValueNode genFloatSub(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
+                return new FloatSubNode(StampFactory.forKind(kind), x, y, isStrictFP);
+            }
+
+            @Override
+            protected ValueNode genFloatMul(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
+                return new FloatMulNode(StampFactory.forKind(kind), x, y, isStrictFP);
+            }
+
+            @Override
+            protected ValueNode genFloatDiv(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
+                return new FloatDivNode(StampFactory.forKind(kind), x, y, isStrictFP);
+            }
+
+            @Override
+            protected ValueNode genFloatRem(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
+                return new FloatRemNode(StampFactory.forKind(kind), x, y, isStrictFP);
+            }
+
+            @Override
+            protected ValueNode genIntegerDiv(Kind kind, ValueNode x, ValueNode y) {
+                return new IntegerDivNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genIntegerRem(Kind kind, ValueNode x, ValueNode y) {
+                return new IntegerRemNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genNegateOp(ValueNode x) {
+                return new NegateNode(x);
+            }
+
+            @Override
+            protected ValueNode genLeftShift(Kind kind, ValueNode x, ValueNode y) {
+                return new LeftShiftNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genRightShift(Kind kind, ValueNode x, ValueNode y) {
+                return new RightShiftNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode genUnsignedRightShift(Kind kind, ValueNode x, ValueNode y) {
+                return new UnsignedRightShiftNode(StampFactory.forKind(kind), x, y);
+            }
+
+            @Override
+            protected ValueNode appendConstant(Constant constant) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            protected ValueNode append(ValueNode v) {
+
+            }
+
+        }
+
         private LineNumberTable lnt;
         private int previousLineNumber;
         private int currentLineNumber;
@@ -204,7 +366,7 @@
             methodSynchronizedObject = null;
             this.currentGraph = graph;
             this.frameState = new HIRFrameStateBuilder(method, graph, graphBuilderConfig.eagerResolving());
-            this.parseHelper = new BytecodeParseHelper<>(frameState);
+            this.parseHelper = new BytecodeParseHelper<ValueNode>(frameState);
             TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method);
             try {
                 build();
@@ -1814,7 +1976,7 @@
                     frameState.insertProxies(x);
                     x.setStateAfter(frameState.create(bci));
                 }
-                processBytecode(bci, opcode);
+                parseHelper.processBytecode(bci, opcode);
 
                 if (lastInstr == null || isBlockEnd(lastInstr) || lastInstr.next() != null) {
                     break;