changeset 21609:2221d959de64

Make BytecodeParser a top-level class to avoid excessive indentation of two class nesting levels
author Christian Wimmer <christian.wimmer@oracle.com>
date Fri, 29 May 2015 19:11:39 -0700
parents 4a8d4ee0fdd6
children f555efead2f2
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParser.java graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java
diffstat 8 files changed, 3563 insertions(+), 3552 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri May 29 19:11:39 2015 -0700
@@ -31,7 +31,7 @@
 
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import static com.oracle.graal.hotspot.replacements.SystemSubstitutions.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
 
 import java.lang.invoke.*;
 import java.util.zip.*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParser.java	Fri May 29 19:11:39 2015 -0700
@@ -0,0 +1,3546 @@
+/*
+ * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.java;
+
+import static com.oracle.graal.bytecode.Bytecodes.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.compiler.common.type.StampFactory.*;
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
+import static com.oracle.graal.nodes.StructuredGraph.*;
+import static com.oracle.graal.nodes.type.StampTool.*;
+import static com.oracle.jvmci.code.TypeCheckHints.*;
+import static com.oracle.jvmci.common.JVMCIError.*;
+import static com.oracle.jvmci.meta.DeoptimizationAction.*;
+import static com.oracle.jvmci.meta.DeoptimizationReason.*;
+import static java.lang.String.*;
+
+import java.util.*;
+
+import com.oracle.graal.bytecode.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.calc.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.Graph.Mark;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.ValueNumberable;
+import com.oracle.graal.graph.iterators.*;
+import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
+import com.oracle.graal.java.BciBlockMapping.BciBlock;
+import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.phases.*;
+import com.oracle.jvmci.code.*;
+import com.oracle.jvmci.common.*;
+import com.oracle.jvmci.debug.*;
+import com.oracle.jvmci.debug.Debug.Scope;
+import com.oracle.jvmci.meta.*;
+import com.oracle.jvmci.options.*;
+
+/**
+ * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph.
+ */
+public class BytecodeParser implements GraphBuilderContext {
+
+    public static class Options {
+        @Option(help = "The trace level for the bytecode parser used when building a graph from bytecode", type = OptionType.Debug)//
+        public static final OptionValue<Integer> TraceBytecodeParserLevel = new OptionValue<>(0);
+
+        @Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert)//
+        public static final StableOptionValue<Boolean> InlineDuringParsing = new StableOptionValue<>(false);
+
+        @Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert)//
+        public static final StableOptionValue<Boolean> InlineIntrinsicsDuringParsing = new StableOptionValue<>(true);
+
+        @Option(help = "Traces inlining performed during bytecode parsing.", type = OptionType.Debug)//
+        public static final StableOptionValue<Boolean> TraceInlineDuringParsing = new StableOptionValue<>(false);
+
+        @Option(help = "Traces use of plugins during bytecode parsing.", type = OptionType.Debug)//
+        public static final StableOptionValue<Boolean> TraceParserPlugins = new StableOptionValue<>(false);
+
+        @Option(help = "Maximum depth when inlining during bytecode parsing.", type = OptionType.Debug)//
+        public static final StableOptionValue<Integer> InlineDuringParsingMaxDepth = new StableOptionValue<>(10);
+
+        @Option(help = "Dump graphs after non-trivial changes during bytecode parsing.", type = OptionType.Debug)//
+        public static final StableOptionValue<Boolean> DumpDuringGraphBuilding = new StableOptionValue<>(false);
+
+        @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)//
+        public static final OptionValue<Integer> MaximumLoopExplosionCount = new OptionValue<>(10000);
+
+        @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug)//
+        public static final OptionValue<Boolean> FailedLoopExplosionIsFatal = new OptionValue<>(false);
+
+        @Option(help = "When creating info points hide the methods of the substitutions.", type = OptionType.Debug)//
+        public static final OptionValue<Boolean> HideSubstitutionStates = new OptionValue<>(false);
+    }
+
+    /**
+     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
+     * bytecode instructions as they are parsed.
+     */
+    public static final int TRACELEVEL_INSTRUCTIONS = 1;
+
+    /**
+     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
+     * frame state before each bytecode instruction as it is parsed.
+     */
+    public static final int TRACELEVEL_STATE = 2;
+
+    /**
+     * Meters the number of actual bytecodes parsed.
+     */
+    public static final DebugMetric BytecodesParsed = Debug.metric("BytecodesParsed");
+
+    protected static final DebugMetric EXPLICIT_EXCEPTIONS = Debug.metric("ExplicitExceptions");
+
+    /**
+     * A scoped object for tasks to be performed after parsing an intrinsic such as processing
+     * {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frames states.
+     */
+    static class IntrinsicScope implements AutoCloseable {
+        FrameState stateBefore;
+        final Mark mark;
+        final BytecodeParser parser;
+
+        /**
+         * Creates a scope for root parsing an intrinsic.
+         *
+         * @param parser the parsing context of the intrinsic
+         */
+        public IntrinsicScope(BytecodeParser parser) {
+            this.parser = parser;
+            assert parser.parent == null;
+            assert parser.bci() == 0;
+            mark = null;
+        }
+
+        /**
+         * Creates a scope for parsing an intrinsic during graph builder inlining.
+         *
+         * @param parser the parsing context of the (non-intrinsic) method calling the intrinsic
+         * @param args the arguments to the call
+         */
+        public IntrinsicScope(BytecodeParser parser, Kind[] argSlotKinds, ValueNode[] args) {
+            assert !parser.parsingIntrinsic();
+            this.parser = parser;
+            mark = parser.getGraph().getMark();
+            stateBefore = parser.frameState.create(parser.bci(), parser.getNonIntrinsicAncestor(), false, argSlotKinds, args);
+        }
+
+        public void close() {
+            IntrinsicContext intrinsic = parser.intrinsicContext;
+            if (intrinsic != null && intrinsic.isPostParseInlined()) {
+                return;
+            }
+
+            processPlaceholderFrameStates(intrinsic);
+        }
+
+        /**
+         * Fixes up the {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frame states
+         * added to the graph while parsing/inlining the intrinsic for which this object exists.
+         */
+        private void processPlaceholderFrameStates(IntrinsicContext intrinsic) {
+            FrameState stateAfterReturn = null;
+            StructuredGraph graph = parser.getGraph();
+            for (Node node : graph.getNewNodes(mark)) {
+                if (node instanceof FrameState) {
+                    FrameState frameState = (FrameState) node;
+                    if (BytecodeFrame.isPlaceholderBci(frameState.bci)) {
+                        if (frameState.bci == BytecodeFrame.AFTER_BCI) {
+                            FrameStateBuilder frameStateBuilder = parser.frameState;
+                            if (frameState.stackSize() != 0) {
+                                assert frameState.usages().count() == 1;
+                                ValueNode returnVal = frameState.stackAt(0);
+                                assert returnVal == frameState.usages().first();
+
+                                /*
+                                 * Swap the top-of-stack value with the side-effect return value
+                                 * using the frame state.
+                                 */
+                                Kind returnKind = parser.currentInvokeReturnType.getKind();
+                                ValueNode tos = frameStateBuilder.pop(returnKind);
+                                assert tos.getKind() == returnVal.getKind();
+                                FrameState newFrameState = frameStateBuilder.create(parser.stream.nextBCI(), parser.getNonIntrinsicAncestor(), false, new Kind[]{returnKind},
+                                                new ValueNode[]{returnVal});
+                                frameState.replaceAndDelete(newFrameState);
+                                frameStateBuilder.push(returnKind, tos);
+                            } else {
+                                if (stateAfterReturn == null) {
+                                    if (intrinsic != null) {
+                                        assert intrinsic.isCompilationRoot();
+                                        stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI));
+                                    } else {
+                                        stateAfterReturn = frameStateBuilder.create(parser.stream.nextBCI(), null);
+                                    }
+                                }
+                                frameState.replaceAndDelete(stateAfterReturn);
+                            }
+                        } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) {
+                            if (stateBefore == null) {
+                                stateBefore = graph.start().stateAfter();
+                            }
+                            if (stateBefore != frameState) {
+                                frameState.replaceAndDelete(stateBefore);
+                            }
+                        } else {
+                            assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static class Target {
+        FixedNode fixed;
+        FrameStateBuilder state;
+
+        public Target(FixedNode fixed, FrameStateBuilder state) {
+            this.fixed = fixed;
+            this.state = state;
+        }
+    }
+
+    private static class ExplodedLoopContext {
+        private BciBlock header;
+        private int[] targetPeelIteration;
+        private int peelIteration;
+    }
+
+    @SuppressWarnings("serial")
+    public static class BytecodeParserError extends JVMCIError {
+
+        public BytecodeParserError(Throwable cause) {
+            super(cause);
+        }
+
+        public BytecodeParserError(String msg, Object... args) {
+            super(msg, args);
+        }
+    }
+
+    private final GraphBuilderPhase.Instance graphBuilderInstance;
+    protected final StructuredGraph graph;
+
+    private BciBlockMapping blockMap;
+    private LocalLiveness liveness;
+    protected final int entryBCI;
+    private final BytecodeParser parent;
+
+    private LineNumberTable lnt;
+    private int previousLineNumber;
+    private int currentLineNumber;
+
+    private ValueNode methodSynchronizedObject;
+
+    private ValueNode returnValue;
+    private FixedWithNextNode beforeReturnNode;
+    private ValueNode unwindValue;
+    private FixedWithNextNode beforeUnwindNode;
+
+    private FixedWithNextNode lastInstr;                 // the last instruction added
+    private final boolean explodeLoops;
+    private final boolean mergeExplosions;
+    private final Map<FrameStateBuilder, Integer> mergeExplosionsMap;
+    private Deque<ExplodedLoopContext> explodeLoopsContext;
+    private int nextPeelIteration = 1;
+    private boolean controlFlowSplit;
+    private final InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(this);
+
+    private FixedWithNextNode[] firstInstructionArray;
+    private FrameStateBuilder[] entryStateArray;
+    private FixedWithNextNode[][] firstInstructionMatrix;
+    private FrameStateBuilder[][] entryStateMatrix;
+
+    protected BytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
+        this.graphBuilderInstance = graphBuilderInstance;
+        this.graph = graph;
+        this.graphBuilderConfig = graphBuilderInstance.graphBuilderConfig;
+        this.optimisticOpts = graphBuilderInstance.optimisticOpts;
+        this.metaAccess = graphBuilderInstance.metaAccess;
+        this.stampProvider = graphBuilderInstance.stampProvider;
+        this.constantReflection = graphBuilderInstance.constantReflection;
+        this.stream = new BytecodeStream(method.getCode());
+        this.profilingInfo = (graphBuilderConfig.getUseProfiling() ? method.getProfilingInfo() : null);
+        this.constantPool = method.getConstantPool();
+        this.method = method;
+        this.intrinsicContext = intrinsicContext;
+        this.entryBCI = entryBCI;
+        this.parent = parent;
+
+        assert method.getCode() != null : "method must contain bytecodes: " + method;
+
+        if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
+            lnt = method.getLineNumberTable();
+            previousLineNumber = -1;
+        }
+
+        LoopExplosionPlugin loopExplosionPlugin = graphBuilderConfig.getPlugins().getLoopExplosionPlugin();
+        if (loopExplosionPlugin != null) {
+            explodeLoops = loopExplosionPlugin.shouldExplodeLoops(method);
+            if (explodeLoops) {
+                mergeExplosions = loopExplosionPlugin.shouldMergeExplosions(method);
+                mergeExplosionsMap = new HashMap<>();
+            } else {
+                mergeExplosions = false;
+                mergeExplosionsMap = null;
+            }
+        } else {
+            explodeLoops = false;
+            mergeExplosions = false;
+            mergeExplosionsMap = null;
+        }
+    }
+
+    public ValueNode getReturnValue() {
+        return returnValue;
+    }
+
+    public FixedWithNextNode getBeforeReturnNode() {
+        return this.beforeReturnNode;
+    }
+
+    public ValueNode getUnwindValue() {
+        return unwindValue;
+    }
+
+    public FixedWithNextNode getBeforeUnwindNode() {
+        return this.beforeUnwindNode;
+    }
+
+    protected void buildRootMethod() {
+        FrameStateBuilder startFrameState = new FrameStateBuilder(this, method, graph);
+        startFrameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugins());
+
+        try (IntrinsicScope s = intrinsicContext != null ? new IntrinsicScope(this) : null) {
+            build(graph.start(), startFrameState);
+        }
+
+        cleanupFinalGraph();
+        ComputeLoopFrequenciesClosure.compute(graph);
+    }
+
+    protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) {
+        if (PrintProfilingInformation.getValue() && profilingInfo != null) {
+            TTY.println("Profiling info for " + method.format("%H.%n(%p)"));
+            TTY.println(MetaUtil.indent(profilingInfo.toString(method, CodeUtil.NEW_LINE), "  "));
+        }
+
+        try (Indent indent = Debug.logAndIndent("build graph for %s", method)) {
+
+            // compute the block map, setup exception handlers and get the entrypoint(s)
+            BciBlockMapping newMapping = BciBlockMapping.create(stream, method);
+            this.blockMap = newMapping;
+            this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()];
+            this.entryStateArray = new FrameStateBuilder[blockMap.getBlockCount()];
+
+            try (Scope s = Debug.scope("LivenessAnalysis")) {
+                int maxLocals = method.getMaxLocals();
+                liveness = LocalLiveness.compute(stream, blockMap.getBlocks(), maxLocals, blockMap.getLoopCount());
+            } catch (Throwable e) {
+                throw Debug.handle(e);
+            }
+
+            lastInstr = startInstruction;
+            this.setCurrentFrameState(startFrameState);
+            stream.setBCI(0);
+
+            BciBlock startBlock = blockMap.getStartBlock();
+            if (startInstruction == graph.start()) {
+                StartNode startNode = graph.start();
+                if (method.isSynchronized()) {
+                    assert !parsingIntrinsic();
+                    startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI, startNode));
+                } else {
+                    if (!parsingIntrinsic()) {
+                        if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
+                            /*
+                             * Don't clear the receiver when Object.<init> is the compilation root.
+                             * The receiver is needed as input to RegisterFinalizerNode.
+                             */
+                        } else {
+                            frameState.clearNonLiveLocals(startBlock, liveness, true);
+                        }
+                        assert bci() == 0;
+                        startNode.setStateAfter(createFrameState(bci(), startNode));
+                    } else {
+                        if (startNode.stateAfter() == null) {
+                            FrameState stateAfterStart = createStateAfterStartOfReplacementGraph();
+                            startNode.setStateAfter(stateAfterStart);
+                        }
+                    }
+                }
+            }
+
+            if (method.isSynchronized()) {
+                // add a monitor enter to the start block
+                methodSynchronizedObject = synchronizedObject(frameState, method);
+                frameState.clearNonLiveLocals(startBlock, liveness, true);
+                assert bci() == 0;
+                genMonitorEnter(methodSynchronizedObject, bci());
+            }
+
+            if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
+                append(createInfoPointNode(InfopointReason.METHOD_START));
+            }
+
+            currentBlock = blockMap.getStartBlock();
+            setEntryState(startBlock, 0, frameState);
+            if (startBlock.isLoopHeader && !explodeLoops) {
+                appendGoto(startBlock);
+            } else {
+                setFirstInstruction(startBlock, 0, lastInstr);
+            }
+
+            int index = 0;
+            BciBlock[] blocks = blockMap.getBlocks();
+            while (index < blocks.length) {
+                BciBlock block = blocks[index];
+                index = iterateBlock(blocks, block);
+            }
+
+            if (this.mergeExplosions) {
+                Debug.dump(graph, "Before loop detection");
+                detectLoops(startInstruction);
+            }
+
+            if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue() && this.beforeReturnNode != startInstruction) {
+                Debug.dump(graph, "Bytecodes parsed: " + method.getDeclaringClass().getUnqualifiedName() + "." + method.getName());
+            }
+        }
+    }
+
+    protected void cleanupFinalGraph() {
+        GraphUtil.normalizeLoops(graph);
+
+        // Remove dead parameters.
+        for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
+            if (param.hasNoUsages()) {
+                assert param.inputs().isEmpty();
+                param.safeDelete();
+            }
+        }
+
+        // Remove redundant begin nodes.
+        Debug.dump(graph, "Before removing redundant begins");
+        for (BeginNode beginNode : graph.getNodes(BeginNode.TYPE)) {
+            Node predecessor = beginNode.predecessor();
+            if (predecessor instanceof ControlSplitNode) {
+                // The begin node is necessary.
+            } else {
+                if (beginNode.hasUsages()) {
+                    reanchorGuardedNodes(beginNode);
+                }
+                GraphUtil.unlinkFixedNode(beginNode);
+                beginNode.safeDelete();
+            }
+        }
+    }
+
+    /**
+     * Removes {@link GuardedNode}s from {@code beginNode}'s usages and re-attaches them to an
+     * appropriate preceeding {@link GuardingNode}.
+     */
+    protected void reanchorGuardedNodes(BeginNode beginNode) {
+        // Find the new guarding node
+        GuardingNode guarding = null;
+        Node pred = beginNode.predecessor();
+        while (pred != null) {
+            if (pred instanceof BeginNode) {
+                if (pred.predecessor() instanceof ControlSplitNode) {
+                    guarding = (GuardingNode) pred;
+                    break;
+                }
+            } else if (pred.getNodeClass().getAllowedUsageTypes().contains(InputType.Guard)) {
+                guarding = (GuardingNode) pred;
+                break;
+            }
+            pred = pred.predecessor();
+        }
+
+        // Reset the guard for all of beginNode's usages
+        for (Node usage : beginNode.usages().snapshot()) {
+            GuardedNode guarded = (GuardedNode) usage;
+            assert guarded.getGuard() == beginNode;
+            guarded.setGuard(guarding);
+        }
+        assert beginNode.hasNoUsages() : beginNode;
+    }
+
+    /**
+     * Creates the frame state after the start node of a graph for an {@link IntrinsicContext
+     * intrinsic} that is the parse root (either for root compiling or for post-parse inlining).
+     */
+    private FrameState createStateAfterStartOfReplacementGraph() {
+        assert parent == null;
+        assert frameState.getMethod().equals(intrinsicContext.getIntrinsicMethod());
+        assert bci() == 0;
+        assert frameState.stackSize() == 0;
+        FrameState stateAfterStart;
+        if (intrinsicContext.isPostParseInlined()) {
+            stateAfterStart = graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
+        } else {
+            ResolvedJavaMethod original = intrinsicContext.getOriginalMethod();
+            ValueNode[] locals;
+            if (original.getMaxLocals() == frameState.localsSize() || original.isNative()) {
+                locals = frameState.locals;
+            } else {
+                locals = new ValueNode[original.getMaxLocals()];
+                int parameterCount = original.getSignature().getParameterCount(!original.isStatic());
+                for (int i = 0; i < parameterCount; i++) {
+                    ValueNode param = frameState.locals[i];
+                    locals[i] = param;
+                    assert param == null || param instanceof ParameterNode || param.isConstant();
+                }
+            }
+            ValueNode[] stack = {};
+            int stackSize = 0;
+            ValueNode[] locks = {};
+            List<MonitorIdNode> monitorIds = Collections.emptyList();
+            stateAfterStart = graph.add(new FrameState(null, original, 0, locals, stack, stackSize, locks, monitorIds, false, false));
+        }
+        return stateAfterStart;
+    }
+
+    private void detectLoops(FixedNode startInstruction) {
+        NodeBitMap visited = graph.createNodeBitMap();
+        NodeBitMap active = graph.createNodeBitMap();
+        Deque<Node> stack = new ArrayDeque<>();
+        stack.add(startInstruction);
+        visited.mark(startInstruction);
+        while (!stack.isEmpty()) {
+            Node next = stack.peek();
+            assert next.isDeleted() || visited.isMarked(next);
+            if (next.isDeleted() || active.isMarked(next)) {
+                stack.pop();
+                if (!next.isDeleted()) {
+                    active.clear(next);
+                }
+            } else {
+                active.mark(next);
+                for (Node n : next.cfgSuccessors()) {
+                    if (active.contains(n)) {
+                        // Detected cycle.
+                        assert n instanceof MergeNode;
+                        assert next instanceof EndNode;
+                        MergeNode merge = (MergeNode) n;
+                        EndNode endNode = (EndNode) next;
+                        merge.removeEnd(endNode);
+                        FixedNode afterMerge = merge.next();
+                        if (!(afterMerge instanceof EndNode) || !(((EndNode) afterMerge).merge() instanceof LoopBeginNode)) {
+                            merge.setNext(null);
+                            LoopBeginNode newLoopBegin = this.appendLoopBegin(merge);
+                            newLoopBegin.setNext(afterMerge);
+                        }
+                        LoopBeginNode loopBegin = (LoopBeginNode) ((EndNode) merge.next()).merge();
+                        LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
+                        if (parsingIntrinsic()) {
+                            loopEnd.disableSafepoint();
+                        }
+                        endNode.replaceAndDelete(loopEnd);
+                    } else if (visited.contains(n)) {
+                        // Normal merge into a branch we are already exploring.
+                    } else {
+                        visited.mark(n);
+                        stack.push(n);
+                    }
+                }
+            }
+        }
+
+        Debug.dump(graph, "After loops detected");
+        insertLoopEnds(startInstruction);
+    }
+
+    private void insertLoopEnds(FixedNode startInstruction) {
+        NodeBitMap visited = graph.createNodeBitMap();
+        Deque<Node> stack = new ArrayDeque<>();
+        stack.add(startInstruction);
+        visited.mark(startInstruction);
+        List<LoopBeginNode> loopBegins = new ArrayList<>();
+        while (!stack.isEmpty()) {
+            Node next = stack.pop();
+            assert visited.isMarked(next);
+            if (next instanceof LoopBeginNode) {
+                loopBegins.add((LoopBeginNode) next);
+            }
+            for (Node n : next.cfgSuccessors()) {
+                if (visited.contains(n)) {
+                    // Nothing to do.
+                } else {
+                    visited.mark(n);
+                    stack.push(n);
+                }
+            }
+        }
+
+        IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap = new IdentityHashMap<>();
+        for (int i = loopBegins.size() - 1; i >= 0; --i) {
+            LoopBeginNode loopBegin = loopBegins.get(i);
+            insertLoopExits(loopBegin, innerLoopsMap);
+            if (DumpDuringGraphBuilding.getValue()) {
+                Debug.dump(graph, "After building loop exits for %s.", loopBegin);
+            }
+        }
+
+        // Remove degenerated merges with only one predecessor.
+        for (LoopBeginNode loopBegin : loopBegins) {
+            Node pred = loopBegin.forwardEnd().predecessor();
+            if (pred instanceof MergeNode) {
+                MergeNode.removeMergeIfDegenerated((MergeNode) pred);
+            }
+        }
+    }
+
+    private void insertLoopExits(LoopBeginNode loopBegin, IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap) {
+        NodeBitMap visited = graph.createNodeBitMap();
+        Deque<Node> stack = new ArrayDeque<>();
+        for (LoopEndNode loopEnd : loopBegin.loopEnds()) {
+            stack.push(loopEnd);
+            visited.mark(loopEnd);
+        }
+
+        List<ControlSplitNode> controlSplits = new ArrayList<>();
+        List<LoopBeginNode> innerLoopBegins = new ArrayList<>();
+
+        while (!stack.isEmpty()) {
+            Node current = stack.pop();
+            if (current == loopBegin) {
+                continue;
+            }
+            for (Node pred : current.cfgPredecessors()) {
+                if (!visited.isMarked(pred)) {
+                    visited.mark(pred);
+                    if (pred instanceof LoopExitNode) {
+                        // Inner loop
+                        LoopExitNode loopExitNode = (LoopExitNode) pred;
+                        LoopBeginNode innerLoopBegin = loopExitNode.loopBegin();
+                        if (!visited.isMarked(innerLoopBegin)) {
+                            stack.push(innerLoopBegin);
+                            visited.mark(innerLoopBegin);
+                            innerLoopBegins.add(innerLoopBegin);
+                        }
+                    } else {
+                        if (pred instanceof ControlSplitNode) {
+                            ControlSplitNode controlSplitNode = (ControlSplitNode) pred;
+                            controlSplits.add(controlSplitNode);
+                        }
+                        stack.push(pred);
+                    }
+                }
+            }
+        }
+
+        for (ControlSplitNode controlSplit : controlSplits) {
+            for (Node succ : controlSplit.cfgSuccessors()) {
+                if (!visited.isMarked(succ)) {
+                    LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
+                    FixedNode next = ((FixedWithNextNode) succ).next();
+                    next.replaceAtPredecessor(loopExit);
+                    loopExit.setNext(next);
+                }
+            }
+        }
+
+        for (LoopBeginNode inner : innerLoopBegins) {
+            addLoopExits(loopBegin, inner, innerLoopsMap, visited);
+            if (DumpDuringGraphBuilding.getValue()) {
+                Debug.dump(graph, "After adding loop exits for %s.", inner);
+            }
+        }
+
+        innerLoopsMap.put(loopBegin, innerLoopBegins);
+    }
+
+    private void addLoopExits(LoopBeginNode loopBegin, LoopBeginNode inner, IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap, NodeBitMap visited) {
+        for (LoopExitNode exit : inner.loopExits()) {
+            if (!visited.isMarked(exit)) {
+                LoopExitNode newLoopExit = graph.add(new LoopExitNode(loopBegin));
+                FixedNode next = exit.next();
+                next.replaceAtPredecessor(newLoopExit);
+                newLoopExit.setNext(next);
+            }
+        }
+
+        for (LoopBeginNode innerInner : innerLoopsMap.get(inner)) {
+            addLoopExits(loopBegin, innerInner, innerLoopsMap, visited);
+        }
+    }
+
+    private int iterateBlock(BciBlock[] blocks, BciBlock block) {
+        if (block.isLoopHeader && this.explodeLoops) {
+            return iterateExplodedLoopHeader(blocks, block);
+        } else {
+            processBlock(this, block);
+            return block.getId() + 1;
+        }
+    }
+
+    private int iterateExplodedLoopHeader(BciBlock[] blocks, BciBlock header) {
+        if (explodeLoopsContext == null) {
+            explodeLoopsContext = new ArrayDeque<>();
+        }
+
+        ExplodedLoopContext context = new ExplodedLoopContext();
+        context.header = header;
+        context.peelIteration = this.getCurrentDimension();
+        if (this.mergeExplosions) {
+            this.addToMergeCache(getEntryState(context.header, context.peelIteration), context.peelIteration);
+        }
+        explodeLoopsContext.push(context);
+        if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
+            Debug.dump(graph, "before loop explosion dimension " + context.peelIteration);
+        }
+        peelIteration(blocks, header, context);
+        explodeLoopsContext.pop();
+        return header.loopEnd + 1;
+    }
+
+    private void addToMergeCache(FrameStateBuilder key, int dimension) {
+        mergeExplosionsMap.put(key, dimension);
+    }
+
+    private void peelIteration(BciBlock[] blocks, BciBlock header, ExplodedLoopContext context) {
+        while (true) {
+            if (TraceParserPlugins.getValue()) {
+                traceWithContext("exploding loop, iteration %d", context.peelIteration);
+            }
+            processBlock(this, header);
+            int j = header.getId() + 1;
+            while (j <= header.loopEnd) {
+                BciBlock block = blocks[j];
+                j = iterateBlock(blocks, block);
+            }
+
+            int[] targets = context.targetPeelIteration;
+            if (targets != null) {
+                // We were reaching the backedge during explosion. Explode further.
+                for (int i = 0; i < targets.length; ++i) {
+                    context.peelIteration = targets[i];
+                    context.targetPeelIteration = null;
+                    if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
+                        Debug.dump(graph, "next loop explosion iteration " + context.peelIteration);
+                    }
+                    if (i < targets.length - 1) {
+                        peelIteration(blocks, header, context);
+                    }
+                }
+            } else {
+                // We did not reach the backedge. Exit.
+                break;
+            }
+        }
+    }
+
+    /**
+     * @param type the unresolved type of the constant
+     */
+    protected void handleUnresolvedLoadConstant(JavaType type) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param type the unresolved type of the type check
+     * @param object the object value whose type is being checked against {@code type}
+     */
+    protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new FixedGuardNode(graph.unique(new IsNullNode(object)), Unresolved, InvalidateRecompile));
+        frameState.push(Kind.Object, appendConstant(JavaConstant.NULL_POINTER));
+    }
+
+    /**
+     * @param type the unresolved type of the type check
+     * @param object the object value whose type is being checked against {@code type}
+     */
+    protected void handleUnresolvedInstanceOf(JavaType type, ValueNode object) {
+        assert !graphBuilderConfig.eagerResolving();
+        AbstractBeginNode successor = graph.add(new BeginNode());
+        DeoptimizeNode deopt = graph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+        append(new IfNode(graph.unique(new IsNullNode(object)), successor, deopt, 1));
+        lastInstr = successor;
+        frameState.push(Kind.Int, appendConstant(JavaConstant.INT_0));
+    }
+
+    /**
+     * @param type the type being instantiated
+     */
+    protected void handleUnresolvedNewInstance(JavaType type) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param type the type of the array being instantiated
+     * @param length the length of the array
+     */
+    protected void handleUnresolvedNewObjectArray(JavaType type, ValueNode length) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param type the type being instantiated
+     * @param dims the dimensions for the multi-array
+     */
+    protected void handleUnresolvedNewMultiArray(JavaType type, List<ValueNode> dims) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param field the unresolved field
+     * @param receiver the object containing the field or {@code null} if {@code field} is static
+     */
+    protected void handleUnresolvedLoadField(JavaField field, ValueNode receiver) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @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 void handleUnresolvedStoreField(JavaField field, ValueNode value, ValueNode receiver) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param type
+     */
+    protected void handleUnresolvedExceptionType(JavaType type) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    /**
+     * @param javaMethod
+     * @param invokeKind
+     */
+    protected void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKind) {
+        assert !graphBuilderConfig.eagerResolving();
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+    }
+
+    private DispatchBeginNode handleException(ValueNode exceptionObject, int bci) {
+        assert bci == BytecodeFrame.BEFORE_BCI || bci == bci() : "invalid bci";
+        Debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci)));
+
+        BciBlock dispatchBlock = currentBlock.exceptionDispatchBlock();
+        /*
+         * The exception dispatch block is always for the last bytecode of a block, so if we are not
+         * at the endBci yet, there is no exception handler for this bci and we can unwind
+         * immediately.
+         */
+        if (bci != currentBlock.endBci || dispatchBlock == null) {
+            dispatchBlock = blockMap.getUnwindBlock();
+        }
+
+        FrameStateBuilder dispatchState = frameState.copy();
+        dispatchState.clearStack();
+
+        DispatchBeginNode dispatchBegin;
+        if (exceptionObject == null) {
+            dispatchBegin = graph.add(new ExceptionObjectNode(metaAccess));
+            dispatchState.push(Kind.Object, dispatchBegin);
+            dispatchState.setRethrowException(true);
+            dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
+        } else {
+            dispatchBegin = graph.add(new DispatchBeginNode());
+            dispatchState.push(Kind.Object, exceptionObject);
+            dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
+            dispatchState.setRethrowException(true);
+        }
+        this.controlFlowSplit = true;
+        FixedNode target = createTarget(dispatchBlock, dispatchState);
+        FixedWithNextNode finishedDispatch = finishInstruction(dispatchBegin, dispatchState);
+        finishedDispatch.setNext(target);
+        return dispatchBegin;
+    }
+
+    protected ValueNode genLoadIndexed(ValueNode array, ValueNode index, Kind kind) {
+        return LoadIndexedNode.create(array, index, kind, metaAccess, constantReflection);
+    }
+
+    protected void genStoreIndexed(ValueNode array, ValueNode index, Kind kind, ValueNode value) {
+        add(new StoreIndexedNode(array, index, kind, value));
+    }
+
+    protected ValueNode genIntegerAdd(ValueNode x, ValueNode y) {
+        return AddNode.create(x, y);
+    }
+
+    protected ValueNode genIntegerSub(ValueNode x, ValueNode y) {
+        return SubNode.create(x, y);
+    }
+
+    protected ValueNode genIntegerMul(ValueNode x, ValueNode y) {
+        return MulNode.create(x, y);
+    }
+
+    protected ValueNode genFloatAdd(ValueNode x, ValueNode y) {
+        return AddNode.create(x, y);
+    }
+
+    protected ValueNode genFloatSub(ValueNode x, ValueNode y) {
+        return SubNode.create(x, y);
+    }
+
+    protected ValueNode genFloatMul(ValueNode x, ValueNode y) {
+        return MulNode.create(x, y);
+    }
+
+    protected ValueNode genFloatDiv(ValueNode x, ValueNode y) {
+        return DivNode.create(x, y);
+    }
+
+    protected ValueNode genFloatRem(ValueNode x, ValueNode y) {
+        return new RemNode(x, y);
+    }
+
+    protected ValueNode genIntegerDiv(ValueNode x, ValueNode y) {
+        return new IntegerDivNode(x, y);
+    }
+
+    protected ValueNode genIntegerRem(ValueNode x, ValueNode y) {
+        return new IntegerRemNode(x, y);
+    }
+
+    protected ValueNode genNegateOp(ValueNode x) {
+        return (new NegateNode(x));
+    }
+
+    protected ValueNode genLeftShift(ValueNode x, ValueNode y) {
+        return new LeftShiftNode(x, y);
+    }
+
+    protected ValueNode genRightShift(ValueNode x, ValueNode y) {
+        return new RightShiftNode(x, y);
+    }
+
+    protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) {
+        return new UnsignedRightShiftNode(x, y);
+    }
+
+    protected ValueNode genAnd(ValueNode x, ValueNode y) {
+        return AndNode.create(x, y);
+    }
+
+    protected ValueNode genOr(ValueNode x, ValueNode y) {
+        return OrNode.create(x, y);
+    }
+
+    protected ValueNode genXor(ValueNode x, ValueNode y) {
+        return XorNode.create(x, y);
+    }
+
+    protected ValueNode genNormalizeCompare(ValueNode x, ValueNode y, boolean isUnorderedLess) {
+        return NormalizeCompareNode.create(x, y, isUnorderedLess, constantReflection);
+    }
+
+    protected ValueNode genFloatConvert(FloatConvert op, ValueNode input) {
+        return FloatConvertNode.create(op, input);
+    }
+
+    protected ValueNode genNarrow(ValueNode input, int bitCount) {
+        return NarrowNode.create(input, bitCount);
+    }
+
+    protected ValueNode genSignExtend(ValueNode input, int bitCount) {
+        return SignExtendNode.create(input, bitCount);
+    }
+
+    protected ValueNode genZeroExtend(ValueNode input, int bitCount) {
+        return ZeroExtendNode.create(input, bitCount);
+    }
+
+    protected void genGoto() {
+        appendGoto(currentBlock.getSuccessor(0));
+        assert currentBlock.numNormalSuccessors() == 1;
+    }
+
+    protected LogicNode genObjectEquals(ValueNode x, ValueNode y) {
+        return ObjectEqualsNode.create(x, y, constantReflection);
+    }
+
+    protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) {
+        return IntegerEqualsNode.create(x, y, constantReflection);
+    }
+
+    protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) {
+        return IntegerLessThanNode.create(x, y, constantReflection);
+    }
+
+    protected ValueNode genUnique(ValueNode x) {
+        return (ValueNode) graph.unique((Node & ValueNumberable) x);
+    }
+
+    protected ValueNode genIfNode(LogicNode condition, FixedNode falseSuccessor, FixedNode trueSuccessor, double d) {
+        return new IfNode(condition, falseSuccessor, trueSuccessor, d);
+    }
+
+    protected void genThrow() {
+        ValueNode exception = frameState.pop(Kind.Object);
+        append(new FixedGuardNode(graph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
+        lastInstr.setNext(handleException(exception, bci()));
+    }
+
+    protected ValueNode createCheckCast(ResolvedJavaType type, ValueNode object, JavaTypeProfile profileForTypeCheck, boolean forStoreCheck) {
+        return CheckCastNode.create(type, object, profileForTypeCheck, forStoreCheck, graph.getAssumptions());
+    }
+
+    protected ValueNode createInstanceOf(ResolvedJavaType type, ValueNode object, JavaTypeProfile profileForTypeCheck) {
+        return InstanceOfNode.create(type, object, profileForTypeCheck);
+    }
+
+    protected ValueNode genConditional(ValueNode x) {
+        return new ConditionalNode((LogicNode) x);
+    }
+
+    protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) {
+        return new NewInstanceNode(type, fillContents);
+    }
+
+    protected NewArrayNode createNewArray(ResolvedJavaType elementType, ValueNode length, boolean fillContents) {
+        return new NewArrayNode(elementType, length, fillContents);
+    }
+
+    protected NewMultiArrayNode createNewMultiArray(ResolvedJavaType type, List<ValueNode> dimensions) {
+        return new NewMultiArrayNode(type, dimensions.toArray(new ValueNode[0]));
+    }
+
+    protected ValueNode genLoadField(ValueNode receiver, ResolvedJavaField field) {
+        return new LoadFieldNode(receiver, field);
+    }
+
+    protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
+        if (StampTool.isPointerNonNull(receiver.stamp())) {
+            return receiver;
+        }
+        BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class));
+        AbstractBeginNode falseSucc = graph.add(new BeginNode());
+        PiNode nonNullReceiver = graph.unique(new PiNode(receiver, receiver.stamp().join(objectNonNull())));
+        nonNullReceiver.setGuard(falseSucc);
+        append(new IfNode(graph.unique(new IsNullNode(receiver)), exception, falseSucc, 0.01));
+        lastInstr = falseSucc;
+
+        exception.setStateAfter(createFrameState(bci(), exception));
+        exception.setNext(handleException(exception, bci()));
+        return nonNullReceiver;
+    }
+
+    protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) {
+        AbstractBeginNode trueSucc = graph.add(new BeginNode());
+        BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
+        append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
+        lastInstr = trueSucc;
+
+        exception.setStateAfter(createFrameState(bci(), exception));
+        exception.setNext(handleException(exception, bci()));
+    }
+
+    protected ValueNode genArrayLength(ValueNode x) {
+        return ArrayLengthNode.create(x, constantReflection);
+    }
+
+    protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) {
+        StoreFieldNode storeFieldNode = new StoreFieldNode(receiver, field, value);
+        append(storeFieldNode);
+        storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI(), storeFieldNode));
+    }
+
+    /**
+     * Ensure that concrete classes are at least linked before generating an invoke. Interfaces may
+     * never be linked so simply return true for them.
+     *
+     * @param target
+     * @return true if the declared holder is an interface or is linked
+     */
+    private static boolean callTargetIsResolved(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
+            ResolvedJavaType resolvedType = resolvedTarget.getDeclaringClass();
+            return resolvedType.isInterface() || resolvedType.isLinked();
+        }
+        return false;
+    }
+
+    protected void genInvokeStatic(JavaMethod target) {
+        if (callTargetIsResolved(target)) {
+            ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
+            ResolvedJavaType holder = resolvedTarget.getDeclaringClass();
+            if (!holder.isInitialized() && ResolveClassBeforeStaticInvoke.getValue()) {
+                handleUnresolvedInvoke(target, InvokeKind.Static);
+            } else {
+                ValueNode[] args = frameState.popArguments(resolvedTarget.getSignature().getParameterCount(false));
+                appendInvoke(InvokeKind.Static, resolvedTarget, args);
+            }
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Static);
+        }
+    }
+
+    protected void genInvokeInterface(JavaMethod target) {
+        if (callTargetIsResolved(target)) {
+            ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
+            appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Interface);
+        }
+    }
+
+    protected void genInvokeDynamic(JavaMethod target) {
+        if (target instanceof ResolvedJavaMethod) {
+            JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI4(), Bytecodes.INVOKEDYNAMIC);
+            if (appendix != null) {
+                frameState.push(Kind.Object, ConstantNode.forConstant(appendix, metaAccess, graph));
+            }
+            ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(false));
+            appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Static);
+        }
+    }
+
+    protected void genInvokeVirtual(JavaMethod target) {
+        if (callTargetIsResolved(target)) {
+            /*
+             * 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 = !((ResolvedJavaMethod) target).isStatic();
+            JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI(), Bytecodes.INVOKEVIRTUAL);
+            if (appendix != null) {
+                frameState.push(Kind.Object, ConstantNode.forConstant(appendix, metaAccess, graph));
+            }
+            ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(hasReceiver));
+            if (hasReceiver) {
+                appendInvoke(InvokeKind.Virtual, (ResolvedJavaMethod) target, args);
+            } else {
+                appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
+            }
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Virtual);
+        }
+
+    }
+
+    protected void genInvokeSpecial(JavaMethod target) {
+        if (callTargetIsResolved(target)) {
+            assert target != null;
+            assert target.getSignature() != null;
+            ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
+            appendInvoke(InvokeKind.Special, (ResolvedJavaMethod) target, args);
+        } else {
+            handleUnresolvedInvoke(target, InvokeKind.Special);
+        }
+    }
+
+    private InvokeKind currentInvokeKind;
+    private JavaType currentInvokeReturnType;
+    protected FrameStateBuilder frameState;
+    protected BciBlock currentBlock;
+    protected final BytecodeStream stream;
+    protected final GraphBuilderConfiguration graphBuilderConfig;
+    protected final ResolvedJavaMethod method;
+    protected final ProfilingInfo profilingInfo;
+    protected final OptimisticOptimizations optimisticOpts;
+    protected final ConstantPool constantPool;
+    protected final MetaAccessProvider metaAccess;
+    private final ConstantReflectionProvider constantReflection;
+    private final StampProvider stampProvider;
+    protected final IntrinsicContext intrinsicContext;
+
+    public InvokeKind getInvokeKind() {
+        return currentInvokeKind;
+    }
+
+    public JavaType getInvokeReturnType() {
+        return currentInvokeReturnType;
+    }
+
+    public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) {
+        appendInvoke(invokeKind, targetMethod, args);
+    }
+
+    private void appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
+        ResolvedJavaMethod targetMethod = initialTargetMethod;
+        InvokeKind invokeKind = initialInvokeKind;
+        if (initialInvokeKind.isIndirect()) {
+            ResolvedJavaType contextType = this.frameState.getMethod().getDeclaringClass();
+            ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(initialInvokeKind, args[0], initialTargetMethod, contextType);
+            if (specialCallTarget != null) {
+                invokeKind = InvokeKind.Special;
+                targetMethod = specialCallTarget;
+            }
+        }
+
+        Kind resultType = targetMethod.getSignature().getReturnKind();
+        if (DeoptALot.getValue()) {
+            append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
+            frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, graph));
+            return;
+        }
+
+        JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass());
+        if (graphBuilderConfig.eagerResolving() || parsingIntrinsic()) {
+            returnType = returnType.resolve(targetMethod.getDeclaringClass());
+        }
+        if (invokeKind.hasReceiver()) {
+            args[0] = emitExplicitExceptions(args[0], null);
+            if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints()) {
+                JavaTypeProfile profile = profilingInfo.getTypeProfile(bci());
+                args[0] = TypeProfileProxyNode.proxify(args[0], profile);
+            }
+
+            if (args[0].isNullConstant()) {
+                append(new DeoptimizeNode(InvalidateRecompile, NullCheckException));
+                return;
+            }
+        }
+
+        try {
+            currentInvokeReturnType = returnType;
+            currentInvokeKind = invokeKind;
+            if (tryGenericInvocationPlugin(args, targetMethod)) {
+                if (TraceParserPlugins.getValue()) {
+                    traceWithContext("used generic invocation plugin for %s", targetMethod.format("%h.%n(%p)"));
+                }
+                return;
+            }
+
+            if (invokeKind.isDirect()) {
+                if (tryInvocationPlugin(args, targetMethod, resultType)) {
+                    if (TraceParserPlugins.getValue()) {
+                        traceWithContext("used invocation plugin for %s", targetMethod.format("%h.%n(%p)"));
+                    }
+                    return;
+                }
+
+                if (tryInline(args, targetMethod, returnType)) {
+                    return;
+                }
+            }
+        } finally {
+            currentInvokeReturnType = null;
+            currentInvokeKind = null;
+        }
+
+        MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, targetMethod, args, returnType));
+
+        // be conservative if information was not recorded (could result in endless
+        // recompiles otherwise)
+        Invoke invoke;
+        if (graphBuilderConfig.omitAllExceptionEdges() ||
+                        (!StressInvokeWithExceptionNode.getValue() && optimisticOpts.useExceptionProbability() && profilingInfo != null && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE)) {
+            invoke = createInvoke(callTarget, resultType);
+        } else {
+            invoke = createInvokeWithException(callTarget, resultType);
+            AbstractBeginNode beginNode = graph.add(new KillingBeginNode(LocationIdentity.any()));
+            invoke.setNext(beginNode);
+            lastInstr = beginNode;
+        }
+
+        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+            plugin.notifyNotInlined(this, targetMethod, invoke);
+        }
+    }
+
+    /**
+     * Contains all the assertion checking logic around the application of an
+     * {@link InvocationPlugin}. This class is only loaded when assertions are enabled.
+     */
+    class InvocationPluginAssertions {
+        final InvocationPlugin plugin;
+        final ValueNode[] args;
+        final ResolvedJavaMethod targetMethod;
+        final Kind resultType;
+        final int beforeStackSize;
+        final boolean needsNullCheck;
+        final int nodeCount;
+        final Mark mark;
+
+        public InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, Kind resultType) {
+            guarantee(assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
+            this.plugin = plugin;
+            this.targetMethod = targetMethod;
+            this.args = args;
+            this.resultType = resultType;
+            this.beforeStackSize = frameState.stackSize();
+            this.needsNullCheck = !targetMethod.isStatic() && args[0].getKind() == Kind.Object && !StampTool.isPointerNonNull(args[0].stamp());
+            this.nodeCount = graph.getNodeCount();
+            this.mark = graph.getMark();
+        }
+
+        String error(String format, Object... a) {
+            return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getApplySourceLocation(metaAccess));
+        }
+
+        boolean check(boolean pluginResult) {
+            if (pluginResult == true) {
+                int expectedStackSize = beforeStackSize + resultType.getSlotCount();
+                assert expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d", expectedStackSize, frameState.stackSize());
+                NodeIterable<Node> newNodes = graph.getNewNodes(mark);
+                assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]);
+                for (Node n : newNodes) {
+                    if (n instanceof StateSplit) {
+                        StateSplit stateSplit = (StateSplit) n;
+                        assert stateSplit.stateAfter() != null || !stateSplit.hasSideEffect() : error("%s node added by plugin for %s need to have a non-null frame state: %s",
+                                        StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit);
+                    }
+                }
+                try {
+                    graphBuilderConfig.getPlugins().getInvocationPlugins().checkNewNodes(BytecodeParser.this, plugin, newNodes);
+                } catch (Throwable t) {
+                    throw new AssertionError(error("Error in plugin"), t);
+                }
+            } else {
+                assert nodeCount == graph.getNodeCount() : error("plugin that returns false must not create new nodes");
+                assert beforeStackSize == frameState.stackSize() : error("plugin that returns false must not modify the stack");
+            }
+            return true;
+        }
+    }
+
+    private boolean tryInvocationPlugin(ValueNode[] args, ResolvedJavaMethod targetMethod, Kind resultType) {
+        InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
+        if (plugin != null) {
+
+            if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) {
+                // Self recursive intrinsic means the original
+                // method should be called.
+                assert !targetMethod.hasBytecodes() : "TODO: when does this happen?";
+                return false;
+            }
+
+            InvocationPluginAssertions assertions = assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
+            if (plugin.execute(this, targetMethod, invocationPluginReceiver.init(targetMethod, args), args)) {
+                assert assertions.check(true);
+                return true;
+            }
+            assert assertions.check(false);
+        }
+        return false;
+    }
+
+    private boolean tryGenericInvocationPlugin(ValueNode[] args, ResolvedJavaMethod targetMethod) {
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleInvoke(this, targetMethod, args)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, JavaType returnType) {
+        boolean canBeInlined = parsingIntrinsic() || targetMethod.canBeInlined();
+        if (!canBeInlined) {
+            return false;
+        }
+        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+            InlineInfo inlineInfo = plugin.shouldInlineInvoke(this, targetMethod, args, returnType);
+            if (inlineInfo != null) {
+                if (inlineInfo.getMethodToInline() == null) {
+                    /* Do not inline, and do not ask the remaining plugins. */
+                    return false;
+                } else {
+                    return inline(targetMethod, inlineInfo.getMethodToInline(), inlineInfo.isIntrinsic(), args);
+                }
+            }
+        }
+        return false;
+    }
+
+    public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) {
+        boolean res = inline(targetMethod, substitute, true, args);
+        assert res : "failed to inline " + substitute;
+    }
+
+    private boolean inline(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) {
+        if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) {
+            if (targetMethod.equals(inlinedMethod)) {
+                traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)"));
+            } else {
+                traceWithContext("inlining call to %s as intrinsic for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)"));
+            }
+        }
+        IntrinsicContext intrinsic = this.intrinsicContext;
+        if (intrinsic != null && intrinsic.isCallToOriginal(targetMethod)) {
+            if (intrinsic.isCompilationRoot()) {
+                // A root compiled intrinsic needs to deoptimize
+                // if the slow path is taken. During frame state
+                // assignment, the deopt node will get its stateBefore
+                // from the start node of the intrinsic
+                append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint));
+                return true;
+            } else {
+                // Otherwise inline the original method. Any frame state created
+                // during the inlining will exclude frame(s) in the
+                // intrinsic method (see HIRFrameStateBuilder.create(int bci)).
+                if (intrinsic.getOriginalMethod().isNative()) {
+                    return false;
+                }
+                parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null);
+                return true;
+            }
+        } else {
+            if (intrinsic == null && isIntrinsic) {
+                assert !inlinedMethod.equals(targetMethod);
+                intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, INLINE_DURING_PARSING);
+            }
+            if (inlinedMethod.hasBytecodes()) {
+                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                    plugin.notifyBeforeInline(targetMethod);
+                }
+                parseAndInlineCallee(inlinedMethod, args, intrinsic);
+                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                    plugin.notifyAfterInline(targetMethod);
+                }
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Prints a line to {@link TTY} with a prefix indicating the current parse context. The prefix
+     * is of the form:
+     *
+     * <pre>
+     * {SPACE * n} {name of method being parsed} "(" {file name} ":" {line number} ")"
+     * </pre>
+     *
+     * where {@code n} is the current inlining depth.
+     *
+     * @param format a format string
+     * @param args arguments to the format string
+     */
+
+    protected void traceWithContext(String format, Object... args) {
+        StackTraceElement where = method.asStackTraceElement(bci());
+        TTY.println(format("%s%s (%s:%d) %s", nSpaces(getDepth()), method.isConstructor() ? method.format("%h.%n") : method.getName(), where.getFileName(), where.getLineNumber(), format(format, args)));
+    }
+
+    protected BytecodeParserError asParserError(Throwable e) {
+        if (e instanceof BytecodeParserError) {
+            return (BytecodeParserError) e;
+        }
+        BytecodeParser bp = this;
+        BytecodeParserError res = new BytecodeParserError(e);
+        while (bp != null) {
+            res.addContext("parsing " + bp.method.asStackTraceElement(bp.bci()));
+            bp = bp.parent;
+        }
+        return res;
+    }
+
+    private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) {
+        try (IntrinsicScope s = calleeIntrinsicContext != null && !parsingIntrinsic() ? new IntrinsicScope(this, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args) : null) {
+
+            BytecodeParser parser = graphBuilderInstance.createBytecodeParser(graph, this, targetMethod, INVOCATION_ENTRY_BCI, calleeIntrinsicContext);
+            FrameStateBuilder startFrameState = new FrameStateBuilder(parser, targetMethod, graph);
+            if (!targetMethod.isStatic()) {
+                args[0] = nullCheckedValue(args[0]);
+            }
+            startFrameState.initializeFromArgumentsArray(args);
+            parser.build(this.lastInstr, startFrameState);
+
+            FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
+            this.lastInstr = calleeBeforeReturnNode;
+            Kind calleeReturnKind = targetMethod.getSignature().getReturnKind();
+            if (calleeBeforeReturnNode != null) {
+                ValueNode calleeReturnValue = parser.getReturnValue();
+                if (calleeReturnValue != null) {
+                    frameState.push(calleeReturnKind.getStackKind(), calleeReturnValue);
+                }
+            }
+
+            FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
+            if (calleeBeforeUnwindNode != null) {
+                ValueNode calleeUnwindValue = parser.getUnwindValue();
+                assert calleeUnwindValue != null;
+                calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
+            }
+
+            // Record inlined method dependency in the graph
+            graph.recordInlinedMethod(targetMethod);
+        }
+    }
+
+    protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] 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);
+        invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
+        return invoke;
+    }
+
+    protected InvokeWithExceptionNode createInvokeWithException(CallTargetNode callTarget, Kind resultType) {
+        if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) {
+            /*
+             * Clear non-live locals early so that the exception handler entry gets the cleared
+             * state.
+             */
+            frameState.clearNonLiveLocals(currentBlock, liveness, false);
+        }
+
+        DispatchBeginNode exceptionEdge = handleException(null, bci());
+        InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
+        frameState.pushReturn(resultType, invoke);
+        invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
+        return invoke;
+    }
+
+    protected void genReturn(ValueNode returnVal, Kind returnKind) {
+        if (parsingIntrinsic() && returnVal != null) {
+            if (returnVal instanceof StateSplit) {
+                StateSplit stateSplit = (StateSplit) returnVal;
+                FrameState stateAfter = stateSplit.stateAfter();
+                if (stateSplit.hasSideEffect()) {
+                    assert stateSplit != null;
+                    if (stateAfter.bci == BytecodeFrame.AFTER_BCI) {
+                        assert stateAfter.usages().count() == 1;
+                        assert stateAfter.usages().first() == stateSplit;
+                        stateAfter.replaceAtUsages(graph.add(new FrameState(BytecodeFrame.AFTER_BCI, returnVal)));
+                        GraphUtil.killWithUnusedFloatingInputs(stateAfter);
+                    } else {
+                        /*
+                         * This must be the return value from within a partial intrinsification.
+                         */
+                        assert !BytecodeFrame.isPlaceholderBci(stateAfter.bci);
+                    }
+                } else {
+                    assert stateAfter == null;
+                }
+            }
+        }
+        if (parent == null) {
+            frameState.setRethrowException(false);
+            frameState.clearStack();
+            beforeReturn(returnVal, returnKind);
+            append(new ReturnNode(returnVal));
+        } else {
+            if (blockMap.getReturnCount() == 1 || !controlFlowSplit) {
+                // There is only a single return.
+                beforeReturn(returnVal, returnKind);
+                this.returnValue = returnVal;
+                this.beforeReturnNode = this.lastInstr;
+                this.lastInstr = null;
+            } else {
+                frameState.setRethrowException(false);
+                frameState.clearStack();
+                if (returnVal != null) {
+                    frameState.push(returnKind, returnVal);
+                }
+                assert blockMap.getReturnCount() > 1;
+                appendGoto(blockMap.getReturnBlock());
+            }
+        }
+    }
+
+    private void beforeReturn(ValueNode x, Kind kind) {
+        if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
+            append(new RegisterFinalizerNode(frameState.loadLocal(0, Kind.Object)));
+        }
+        if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
+            append(createInfoPointNode(InfopointReason.METHOD_END));
+        }
+
+        synchronizedEpilogue(BytecodeFrame.AFTER_BCI, x, kind);
+        if (frameState.lockDepth() != 0) {
+            throw bailout("unbalanced monitors");
+        }
+    }
+
+    protected void genMonitorEnter(ValueNode x, int bci) {
+        MonitorIdNode monitorId = graph.add(new MonitorIdNode(frameState.lockDepth()));
+        MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, monitorId));
+        frameState.pushLock(x, monitorId);
+        monitorEnter.setStateAfter(createFrameState(bci, monitorEnter));
+    }
+
+    protected void genMonitorExit(ValueNode x, ValueNode escapedReturnValue, int bci) {
+        MonitorIdNode monitorId = frameState.peekMonitorId();
+        ValueNode lockedObject = frameState.popLock();
+        if (GraphUtil.originalValue(lockedObject) != GraphUtil.originalValue(x)) {
+            throw bailout(String.format("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject)));
+        }
+        MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, escapedReturnValue));
+        monitorExit.setStateAfter(createFrameState(bci, monitorExit));
+    }
+
+    protected void genJsr(int dest) {
+        BciBlock successor = currentBlock.getJsrSuccessor();
+        assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci();
+        JsrScope scope = currentBlock.getJsrScope();
+        int nextBci = getStream().nextBCI();
+        if (!successor.getJsrScope().pop().equals(scope)) {
+            throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
+        }
+        if (successor.getJsrScope().nextReturnAddress() != nextBci) {
+            throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
+        }
+        ConstantNode nextBciNode = getJsrConstant(nextBci);
+        frameState.push(Kind.Object, nextBciNode);
+        appendGoto(successor);
+    }
+
+    protected void genRet(int localIndex) {
+        BciBlock successor = currentBlock.getRetSuccessor();
+        ValueNode local = frameState.loadLocal(localIndex, Kind.Object);
+        JsrScope scope = currentBlock.getJsrScope();
+        int retAddress = scope.nextReturnAddress();
+        ConstantNode returnBciNode = getJsrConstant(retAddress);
+        LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflection);
+        guard = graph.unique(guard);
+        append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
+        if (!successor.getJsrScope().equals(scope.pop())) {
+            throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
+        }
+        appendGoto(successor);
+    }
+
+    private ConstantNode getJsrConstant(long bci) {
+        JavaConstant nextBciConstant = new RawConstant(bci);
+        Stamp nextBciStamp = StampFactory.forConstant(nextBciConstant);
+        ConstantNode nextBciNode = new ConstantNode(nextBciConstant, nextBciStamp);
+        return graph.unique(nextBciNode);
+    }
+
+    protected void genIntegerSwitch(ValueNode value, ArrayList<BciBlock> actualSuccessors, int[] keys, double[] keyProbabilities, int[] keySuccessors) {
+        if (value.isConstant()) {
+            JavaConstant constant = (JavaConstant) value.asConstant();
+            int constantValue = constant.asInt();
+            for (int i = 0; i < keys.length; ++i) {
+                if (keys[i] == constantValue) {
+                    appendGoto(actualSuccessors.get(keySuccessors[i]));
+                    return;
+                }
+            }
+            appendGoto(actualSuccessors.get(keySuccessors[keys.length]));
+        } else {
+            this.controlFlowSplit = true;
+            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));
+            }
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+    protected ConstantNode appendConstant(JavaConstant constant) {
+        assert constant != null;
+        return ConstantNode.forConstant(constant, metaAccess, graph);
+    }
+
+    @Override
+    public <T extends ValueNode> T append(T v) {
+        if (v.graph() != null) {
+            return v;
+        }
+        T added = graph.addOrUnique(v);
+        if (added == v) {
+            updateLastInstruction(v);
+        }
+        return added;
+    }
+
+    public <T extends ValueNode> T recursiveAppend(T v) {
+        if (v.graph() != null) {
+            return v;
+        }
+        T added = graph.addOrUniqueWithInputs(v);
+        if (added == v) {
+            updateLastInstruction(v);
+        }
+        return added;
+    }
+
+    private <T extends ValueNode> void updateLastInstruction(T v) {
+        if (v instanceof FixedNode) {
+            FixedNode fixedNode = (FixedNode) v;
+            lastInstr.setNext(fixedNode);
+            if (fixedNode instanceof FixedWithNextNode) {
+                FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode;
+                assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end";
+                lastInstr = fixedWithNextNode;
+            } else {
+                lastInstr = null;
+            }
+        }
+    }
+
+    private Target checkLoopExit(FixedNode target, BciBlock targetBlock, FrameStateBuilder state) {
+        if (currentBlock != null && !explodeLoops) {
+            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(blockMap.getLoopHeader(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;
+                }
+                FrameStateBuilder newState = state.copy();
+                for (BciBlock loop : exitLoops) {
+                    LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(loop, this.getCurrentDimension());
+                    LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
+                    if (lastLoopExit != null) {
+                        lastLoopExit.setNext(loopExit);
+                    }
+                    if (firstLoopExit == null) {
+                        firstLoopExit = loopExit;
+                    }
+                    lastLoopExit = loopExit;
+                    Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
+                    newState.insertLoopProxies(loopExit, getEntryState(loop, this.getCurrentDimension()));
+                    loopExit.setStateAfter(newState.create(bci, loopExit));
+                }
+
+                lastLoopExit.setNext(target);
+                return new Target(firstLoopExit, newState);
+            }
+        }
+        return new Target(target, state);
+    }
+
+    private FrameStateBuilder getEntryState(BciBlock block, int dimension) {
+        int id = block.id;
+        if (dimension == 0) {
+            return entryStateArray[id];
+        } else {
+            return getEntryStateMultiDimension(dimension, id);
+        }
+    }
+
+    private FrameStateBuilder getEntryStateMultiDimension(int dimension, int id) {
+        if (entryStateMatrix != null && dimension - 1 < entryStateMatrix.length) {
+            FrameStateBuilder[] entryStateArrayEntry = entryStateMatrix[dimension - 1];
+            if (entryStateArrayEntry == null) {
+                return null;
+            }
+            return entryStateArrayEntry[id];
+        } else {
+            return null;
+        }
+    }
+
+    private void setEntryState(BciBlock block, int dimension, FrameStateBuilder entryState) {
+        int id = block.id;
+        if (dimension == 0) {
+            this.entryStateArray[id] = entryState;
+        } else {
+            setEntryStateMultiDimension(dimension, entryState, id);
+        }
+    }
+
+    private void setEntryStateMultiDimension(int dimension, FrameStateBuilder entryState, int id) {
+        if (entryStateMatrix == null) {
+            entryStateMatrix = new FrameStateBuilder[4][];
+        }
+        if (dimension - 1 < entryStateMatrix.length) {
+            // We are within bounds.
+        } else {
+            // We are out of bounds.
+            entryStateMatrix = Arrays.copyOf(entryStateMatrix, Math.max(entryStateMatrix.length * 2, dimension));
+        }
+        if (entryStateMatrix[dimension - 1] == null) {
+            entryStateMatrix[dimension - 1] = new FrameStateBuilder[blockMap.getBlockCount()];
+        }
+        entryStateMatrix[dimension - 1][id] = entryState;
+    }
+
+    private void setFirstInstruction(BciBlock block, int dimension, FixedWithNextNode firstInstruction) {
+        int id = block.id;
+        if (dimension == 0) {
+            this.firstInstructionArray[id] = firstInstruction;
+        } else {
+            setFirstInstructionMultiDimension(dimension, firstInstruction, id);
+        }
+    }
+
+    private void setFirstInstructionMultiDimension(int dimension, FixedWithNextNode firstInstruction, int id) {
+        if (firstInstructionMatrix == null) {
+            firstInstructionMatrix = new FixedWithNextNode[4][];
+        }
+        if (dimension - 1 < firstInstructionMatrix.length) {
+            // We are within bounds.
+        } else {
+            // We are out of bounds.
+            firstInstructionMatrix = Arrays.copyOf(firstInstructionMatrix, Math.max(firstInstructionMatrix.length * 2, dimension));
+        }
+        if (firstInstructionMatrix[dimension - 1] == null) {
+            firstInstructionMatrix[dimension - 1] = new FixedWithNextNode[blockMap.getBlockCount()];
+        }
+        firstInstructionMatrix[dimension - 1][id] = firstInstruction;
+    }
+
+    private FixedWithNextNode getFirstInstruction(BciBlock block, int dimension) {
+        int id = block.id;
+        if (dimension == 0) {
+            return firstInstructionArray[id];
+        } else {
+            return getFirstInstructionMultiDimension(dimension, id);
+        }
+    }
+
+    private FixedWithNextNode getFirstInstructionMultiDimension(int dimension, int id) {
+        if (firstInstructionMatrix != null && dimension - 1 < firstInstructionMatrix.length) {
+            FixedWithNextNode[] firstInstructionArrayEntry = firstInstructionMatrix[dimension - 1];
+            if (firstInstructionArrayEntry == null) {
+                return null;
+            }
+            return firstInstructionArrayEntry[id];
+        } else {
+            return null;
+        }
+    }
+
+    private FixedNode createTarget(double probability, BciBlock block, FrameStateBuilder stateAfter) {
+        assert probability >= 0 && probability <= 1.01 : probability;
+        if (isNeverExecutedCode(probability)) {
+            return graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+        } else {
+            assert block != null;
+            return createTarget(block, stateAfter);
+        }
+    }
+
+    private FixedNode createTarget(BciBlock block, FrameStateBuilder state) {
+        return createTarget(block, state, false, false);
+    }
+
+    private FixedNode createTarget(BciBlock block, FrameStateBuilder state, boolean canReuseInstruction, boolean canReuseState) {
+        assert block != null && state != null;
+        assert !block.isExceptionEntry || state.stackSize() == 1;
+
+        int operatingDimension = findOperatingDimension(block, state);
+
+        if (getFirstInstruction(block, operatingDimension) == 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.
+             */
+            FixedNode targetNode;
+            if (canReuseInstruction && (block.getPredecessorCount() == 1 || !controlFlowSplit) && !block.isLoopHeader && (currentBlock.loops & ~block.loops) == 0) {
+                setFirstInstruction(block, operatingDimension, lastInstr);
+                lastInstr = null;
+            } else {
+                setFirstInstruction(block, operatingDimension, graph.add(new BeginNode()));
+            }
+            targetNode = getFirstInstruction(block, operatingDimension);
+            Target target = checkLoopExit(targetNode, block, state);
+            FixedNode result = target.fixed;
+            FrameStateBuilder currentEntryState = target.state == state ? (canReuseState ? state : state.copy()) : target.state;
+            setEntryState(block, operatingDimension, currentEntryState);
+            currentEntryState.clearNonLiveLocals(block, liveness, true);
+
+            Debug.log("createTarget %s: first visit, result: %s", block, targetNode);
+            return result;
+        }
+
+        // We already saw this block before, so we have to merge states.
+        if (!getEntryState(block, operatingDimension).isCompatibleWith(state)) {
+            throw bailout("stacks do not match; bytecodes would not verify");
+        }
+
+        if (getFirstInstruction(block, operatingDimension) instanceof LoopBeginNode) {
+            assert this.explodeLoops || (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) getFirstInstruction(block, operatingDimension);
+            LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
+            if (parsingIntrinsic()) {
+                loopEnd.disableSafepoint();
+            }
+            Target target = checkLoopExit(loopEnd, block, state);
+            FixedNode result = target.fixed;
+            getEntryState(block, operatingDimension).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() || this.mergeExplosions : "must not be backward branch";
+        assert getFirstInstruction(block, operatingDimension).next() == null || this.mergeExplosions : "bytecodes already parsed for block";
+
+        if (getFirstInstruction(block, operatingDimension) instanceof AbstractBeginNode && !(getFirstInstruction(block, operatingDimension) instanceof AbstractMergeNode)) {
+            /*
+             * This is the second time we see this block. Create the actual MergeNode and the End
+             * Node for the already existing edge.
+             */
+            AbstractBeginNode beginNode = (AbstractBeginNode) getFirstInstruction(block, operatingDimension);
+
+            // The EndNode for the already existing edge.
+            EndNode end = graph.add(new EndNode());
+            // The MergeNode that replaces the placeholder.
+            AbstractMergeNode mergeNode = graph.add(new MergeNode());
+            FixedNode next = beginNode.next();
+
+            if (beginNode.predecessor() instanceof ControlSplitNode) {
+                beginNode.setNext(end);
+            } else {
+                beginNode.replaceAtPredecessor(end);
+                beginNode.safeDelete();
+            }
+
+            mergeNode.addForwardEnd(end);
+            mergeNode.setNext(next);
+
+            setFirstInstruction(block, operatingDimension, mergeNode);
+        }
+
+        AbstractMergeNode mergeNode = (AbstractMergeNode) getFirstInstruction(block, operatingDimension);
+
+        // The EndNode for the newly merged edge.
+        EndNode newEnd = graph.add(new EndNode());
+        Target target = checkLoopExit(newEnd, block, state);
+        FixedNode result = target.fixed;
+        getEntryState(block, operatingDimension).merge(mergeNode, target.state);
+        mergeNode.addForwardEnd(newEnd);
+
+        Debug.log("createTarget %s: merging state, result: %s", block, result);
+        return result;
+    }
+
+    private int findOperatingDimension(BciBlock block, FrameStateBuilder state) {
+        if (this.explodeLoops && this.explodeLoopsContext != null && !this.explodeLoopsContext.isEmpty()) {
+            return findOperatingDimensionWithLoopExplosion(block, state);
+        }
+        return this.getCurrentDimension();
+    }
+
+    private int findOperatingDimensionWithLoopExplosion(BciBlock block, FrameStateBuilder state) {
+        for (ExplodedLoopContext context : explodeLoopsContext) {
+            if (context.header == block) {
+
+                if (this.mergeExplosions) {
+                    state.clearNonLiveLocals(block, liveness, true);
+                    Integer cachedDimension = mergeExplosionsMap.get(state);
+                    if (cachedDimension != null) {
+                        return cachedDimension;
+                    }
+                }
+
+                // We have a hit on our current explosion context loop begin.
+                if (context.targetPeelIteration == null) {
+                    context.targetPeelIteration = new int[1];
+                } else {
+                    context.targetPeelIteration = Arrays.copyOf(context.targetPeelIteration, context.targetPeelIteration.length + 1);
+                }
+
+                // This is the first hit => allocate a new dimension and at the same
+                // time mark the context loop begin as hit during the current
+                // iteration.
+                if (this.mergeExplosions) {
+                    this.addToMergeCache(state, nextPeelIteration);
+                }
+                context.targetPeelIteration[context.targetPeelIteration.length - 1] = nextPeelIteration++;
+                if (nextPeelIteration > MaximumLoopExplosionCount.getValue()) {
+                    String message = "too many loop explosion iterations - does the explosion not terminate for method " + method + "?";
+                    if (FailedLoopExplosionIsFatal.getValue()) {
+                        throw new RuntimeException(message);
+                    } else {
+                        throw bailout(message);
+                    }
+                }
+
+                // Operate on the target dimension.
+                return context.targetPeelIteration[context.targetPeelIteration.length - 1];
+            } else if (block.getId() > context.header.getId() && block.getId() <= context.header.loopEnd) {
+                // We hit the range of this context.
+                return context.peelIteration;
+            }
+        }
+
+        // No dimension found.
+        return 0;
+    }
+
+    /**
+     * 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, FrameStateBuilder stateAfter) {
+        FixedNode target = createTarget(probability, block, stateAfter);
+        AbstractBeginNode begin = BeginNode.begin(target);
+
+        assert !(target instanceof DeoptimizeNode && begin instanceof BeginStateSplitNode && ((BeginStateSplitNode) 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 ValueNode synchronizedObject(FrameStateBuilder state, ResolvedJavaMethod target) {
+        if (target.isStatic()) {
+            return appendConstant(target.getDeclaringClass().getJavaClass());
+        } else {
+            return state.loadLocal(0, Kind.Object);
+        }
+    }
+
+    protected void processBlock(BytecodeParser parser, BciBlock block) {
+        // Ignore blocks that have no predecessors by the time their bytecodes are parsed
+        int currentDimension = this.getCurrentDimension();
+        FixedWithNextNode firstInstruction = getFirstInstruction(block, currentDimension);
+        if (firstInstruction == null) {
+            Debug.log("Ignoring block %s", block);
+            return;
+        }
+        try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, firstInstruction, block.isLoopHeader)) {
+
+            lastInstr = firstInstruction;
+            frameState = getEntryState(block, currentDimension);
+            parser.setCurrentFrameState(frameState);
+            currentBlock = block;
+
+            if (firstInstruction instanceof AbstractMergeNode) {
+                setMergeStateAfter(block, firstInstruction);
+            }
+
+            if (block == blockMap.getReturnBlock()) {
+                handleReturnBlock();
+            } else if (block == blockMap.getUnwindBlock()) {
+                handleUnwindBlock();
+            } else if (block instanceof ExceptionDispatchBlock) {
+                createExceptionDispatch((ExceptionDispatchBlock) block);
+            } else {
+                frameState.setRethrowException(false);
+                iterateBytecodesForBlock(block);
+            }
+        }
+    }
+
+    private void handleUnwindBlock() {
+        if (parent == null) {
+            frameState.setRethrowException(false);
+            createUnwind();
+        } else {
+            ValueNode exception = frameState.pop(Kind.Object);
+            this.unwindValue = exception;
+            this.beforeUnwindNode = this.lastInstr;
+        }
+    }
+
+    private void handleReturnBlock() {
+        Kind returnKind = method.getSignature().getReturnKind().getStackKind();
+        ValueNode x = returnKind == Kind.Void ? null : frameState.pop(returnKind);
+        assert frameState.stackSize() == 0;
+        beforeReturn(x, returnKind);
+        this.returnValue = x;
+        this.beforeReturnNode = this.lastInstr;
+    }
+
+    private void setMergeStateAfter(BciBlock block, FixedWithNextNode firstInstruction) {
+        AbstractMergeNode abstractMergeNode = (AbstractMergeNode) firstInstruction;
+        if (abstractMergeNode.stateAfter() == null) {
+            int bci = block.startBci;
+            if (block instanceof ExceptionDispatchBlock) {
+                bci = ((ExceptionDispatchBlock) block).deoptBci;
+            }
+            abstractMergeNode.setStateAfter(createFrameState(bci, abstractMergeNode));
+        }
+    }
+
+    private void createUnwind() {
+        assert frameState.stackSize() == 1 : frameState;
+        ValueNode exception = frameState.pop(Kind.Object);
+        synchronizedEpilogue(BytecodeFrame.AFTER_EXCEPTION_BCI, null, null);
+        append(new UnwindNode(exception));
+    }
+
+    private void synchronizedEpilogue(int bci, ValueNode currentReturnValue, Kind currentReturnValueKind) {
+        if (method.isSynchronized()) {
+            if (currentReturnValue != null) {
+                frameState.push(currentReturnValueKind, currentReturnValue);
+            }
+            genMonitorExit(methodSynchronizedObject, currentReturnValue, bci);
+            assert !frameState.rethrowException();
+        }
+    }
+
+    private void createExceptionDispatch(ExceptionDispatchBlock block) {
+        assert frameState.stackSize() == 1 : frameState;
+        if (block.handler.isCatchAll()) {
+            assert block.getSuccessorCount() == 1;
+            appendGoto(block.getSuccessor(0));
+            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 ? blockMap.getUnwindBlock() : block.getSuccessor(1);
+                    ValueNode exception = frameState.stack[0];
+                    FixedNode trueSuccessor = graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+                    FixedNode nextDispatch = createTarget(nextBlock, frameState);
+                    append(new IfNode(graph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), trueSuccessor, nextDispatch, 0));
+                    return;
+                }
+            }
+        }
+
+        if (initialized) {
+            BciBlock nextBlock = block.getSuccessorCount() == 1 ? blockMap.getUnwindBlock() : block.getSuccessor(1);
+            ValueNode exception = frameState.stack[0];
+            CheckCastNode checkCast = graph.add(new CheckCastNode((ResolvedJavaType) catchType, exception, null, false));
+            frameState.pop(Kind.Object);
+            frameState.push(Kind.Object, checkCast);
+            FixedNode catchSuccessor = createTarget(block.getSuccessor(0), frameState);
+            frameState.pop(Kind.Object);
+            frameState.push(Kind.Object, exception);
+            FixedNode nextDispatch = createTarget(nextBlock, frameState);
+            checkCast.setNext(catchSuccessor);
+            append(new IfNode(graph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
+        } else {
+            handleUnresolvedExceptionType(catchType);
+        }
+    }
+
+    private void appendGoto(BciBlock successor) {
+        FixedNode targetInstr = createTarget(successor, frameState, true, true);
+        if (lastInstr != null && lastInstr != targetInstr) {
+            lastInstr.setNext(targetInstr);
+        }
+    }
+
+    protected void iterateBytecodesForBlock(BciBlock block) {
+        if (block.isLoopHeader && !explodeLoops) {
+            // Create the loop header block, which later will merge the backward branches of
+            // the loop.
+            controlFlowSplit = true;
+            LoopBeginNode loopBegin = appendLoopBegin(this.lastInstr);
+            lastInstr = loopBegin;
+
+            // Create phi functions for all local variables and operand stack slots.
+            frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis());
+            loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin));
+
+            /*
+             * 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.
+             */
+            setFirstInstruction(block, this.getCurrentDimension(), 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.
+             */
+            setEntryState(block, this.getCurrentDimension(), frameState.copy());
+
+            Debug.log("  created loop header %s", loopBegin);
+        } else if (block.isLoopHeader && explodeLoops && this.mergeExplosions) {
+            frameState = frameState.copy();
+        }
+        assert lastInstr.next() == null : "instructions already appended at block " + block;
+        Debug.log("  frameState: %s", frameState);
+
+        lastInstr = finishInstruction(lastInstr, frameState);
+
+        int endBCI = stream.endBCI();
+
+        stream.setBCI(block.startBci);
+        int bci = block.startBci;
+        BytecodesParsed.add(block.endBci - bci);
+
+        /* Reset line number for new block */
+        if (graphBuilderConfig.insertSimpleDebugInfo()) {
+            previousLineNumber = -1;
+        }
+
+        while (bci < endBCI) {
+            if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
+                currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : (graphBuilderConfig.insertFullDebugInfo() ? -1 : bci);
+                if (currentLineNumber != previousLineNumber) {
+                    append(createInfoPointNode(InfopointReason.LINE_NUMBER));
+                    previousLineNumber = currentLineNumber;
+                }
+            }
+
+            // read the opcode
+            int opcode = stream.currentBC();
+            assert traceState();
+            assert traceInstruction(bci, opcode, bci == block.startBci);
+            if (parent == null && bci == entryBCI) {
+                if (block.getJsrScope() != JsrScope.EMPTY_SCOPE) {
+                    throw new BailoutException("OSR into a JSR scope is not supported");
+                }
+                EntryMarkerNode x = append(new EntryMarkerNode());
+                frameState.insertProxies(x);
+                x.setStateAfter(createFrameState(bci, x));
+            }
+
+            try {
+                processBytecode(bci, opcode);
+            } catch (Throwable e) {
+                throw asParserError(e);
+            }
+
+            if (lastInstr == null || lastInstr.next() != null) {
+                break;
+            }
+
+            stream.next();
+            bci = stream.currentBCI();
+
+            assert block == currentBlock;
+            assert checkLastInstruction();
+            lastInstr = finishInstruction(lastInstr, frameState);
+            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(block.getSuccessor(0));
+                    break;
+                }
+            }
+        }
+    }
+
+    /* Also a hook for subclasses. */
+    protected boolean forceLoopPhis() {
+        return graph.isOSR();
+    }
+
+    protected boolean checkLastInstruction() {
+        if (lastInstr instanceof BeginNode) {
+            // ignore
+        } else if (lastInstr instanceof StateSplit) {
+            StateSplit stateSplit = (StateSplit) lastInstr;
+            if (stateSplit.hasSideEffect()) {
+                assert stateSplit.stateAfter() != null : "side effect " + lastInstr + " requires a non-null stateAfter";
+            }
+        }
+        return true;
+    }
+
+    private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext) {
+        EndNode preLoopEnd = graph.add(new EndNode());
+        LoopBeginNode loopBegin = graph.add(new LoopBeginNode());
+        fixedWithNext.setNext(preLoopEnd);
+        // Add the single non-loop predecessor of the loop header.
+        loopBegin.addForwardEnd(preLoopEnd);
+        return loopBegin;
+    }
+
+    /**
+     * A hook for derived classes to modify the last instruction or add other instructions.
+     *
+     * @param instr The last instruction (= fixed node) which was added.
+     * @param state The current frame state.
+     * @return Returns the (new) last instruction.
+     */
+    protected FixedWithNextNode finishInstruction(FixedWithNextNode instr, FrameStateBuilder state) {
+        return instr;
+    }
+
+    private InfopointNode createInfoPointNode(InfopointReason reason) {
+        if (graphBuilderConfig.insertFullDebugInfo()) {
+            return new FullInfopointNode(reason, createFrameState(bci(), null));
+        } else {
+            BytecodePosition position = createBytecodePosition();
+            // Update the previous infopoint position if no new fixed nodes were inserted
+            if (lastInstr instanceof SimpleInfopointNode) {
+                SimpleInfopointNode lastInfopoint = (SimpleInfopointNode) lastInstr;
+                if (lastInfopoint.getReason() == reason) {
+                    lastInfopoint.setPosition(position);
+                    return lastInfopoint;
+                }
+            }
+            return new SimpleInfopointNode(reason, position);
+        }
+    }
+
+    private boolean traceState() {
+        if (Debug.isEnabled() && Options.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
+            frameState.traceState();
+        }
+        return true;
+    }
+
+    protected void genIf(ValueNode x, Condition cond, ValueNode y) {
+        assert x.getKind().getStackKind() == y.getKind().getStackKind();
+        assert currentBlock.getSuccessorCount() == 2;
+        BciBlock trueBlock = currentBlock.getSuccessor(0);
+        BciBlock falseBlock = currentBlock.getSuccessor(1);
+        if (trueBlock == falseBlock) {
+            // The target block is the same independent of the condition.
+            appendGoto(trueBlock);
+            return;
+        }
+
+        ValueNode a = x;
+        ValueNode b = y;
+
+        // Check whether the condition needs to mirror the operands.
+        if (cond.canonicalMirror()) {
+            a = y;
+            b = x;
+        }
+
+        // Create the logic node for the condition.
+        LogicNode condition = createLogicNode(cond, a, b);
+
+        // Check whether the condition needs to negate the result.
+        boolean negate = cond.canonicalNegate();
+
+        // Remove a logic negation node and fold it into the negate boolean.
+        if (condition instanceof LogicNegationNode) {
+            LogicNegationNode logicNegationNode = (LogicNegationNode) condition;
+            negate = !negate;
+            condition = logicNegationNode.getValue();
+        }
+
+        if (condition instanceof LogicConstantNode) {
+            genConstantTargetIf(trueBlock, falseBlock, negate, condition);
+        } else {
+            if (condition.graph() == null) {
+                condition = graph.unique(condition);
+            }
+
+            // Need to get probability based on current bci.
+            double probability = branchProbability();
+
+            if (negate) {
+                BciBlock tmpBlock = trueBlock;
+                trueBlock = falseBlock;
+                falseBlock = tmpBlock;
+                probability = 1 - probability;
+            }
+
+            if (isNeverExecutedCode(probability)) {
+                append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, true));
+                appendGoto(falseBlock);
+                return;
+            } else if (isNeverExecutedCode(1 - probability)) {
+                append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, false));
+                appendGoto(trueBlock);
+                return;
+            }
+
+            int oldBci = stream.currentBCI();
+            int trueBlockInt = checkPositiveIntConstantPushed(trueBlock);
+            if (trueBlockInt != -1) {
+                int falseBlockInt = checkPositiveIntConstantPushed(falseBlock);
+                if (falseBlockInt != -1) {
+                    if (tryGenConditionalForIf(trueBlock, falseBlock, condition, oldBci, trueBlockInt, falseBlockInt)) {
+                        return;
+                    }
+                }
+            }
+
+            this.controlFlowSplit = true;
+            FixedNode trueSuccessor = createTarget(trueBlock, frameState, false, false);
+            FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true);
+            ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability);
+            append(ifNode);
+            if (parsingIntrinsic()) {
+                if (x instanceof BranchProbabilityNode) {
+                    ((BranchProbabilityNode) x).simplify(null);
+                } else if (y instanceof BranchProbabilityNode) {
+                    ((BranchProbabilityNode) y).simplify(null);
+                }
+            }
+        }
+    }
+
+    private boolean tryGenConditionalForIf(BciBlock trueBlock, BciBlock falseBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt) {
+        if (gotoOrFallThroughAfterConstant(trueBlock) && gotoOrFallThroughAfterConstant(falseBlock) && trueBlock.getSuccessor(0) == falseBlock.getSuccessor(0)) {
+            genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, false);
+            return true;
+        } else if (this.parent != null && returnAfterConstant(trueBlock) && returnAfterConstant(falseBlock)) {
+            genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, true);
+            return true;
+        }
+        return false;
+    }
+
+    private void genConditionalForIf(BciBlock trueBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt, boolean genReturn) {
+        ConstantNode trueValue = graph.unique(ConstantNode.forInt(trueBlockInt));
+        ConstantNode falseValue = graph.unique(ConstantNode.forInt(falseBlockInt));
+        ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue);
+        if (conditionalNode.graph() == null) {
+            conditionalNode = graph.addOrUnique(conditionalNode);
+        }
+        if (genReturn) {
+            Kind returnKind = method.getSignature().getReturnKind().getStackKind();
+            this.genReturn(conditionalNode, returnKind);
+        } else {
+            frameState.push(Kind.Int, conditionalNode);
+            appendGoto(trueBlock.getSuccessor(0));
+            stream.setBCI(oldBci);
+        }
+    }
+
+    private LogicNode createLogicNode(Condition cond, ValueNode a, ValueNode b) {
+        LogicNode condition;
+        assert !a.getKind().isNumericFloat();
+        if (cond == Condition.EQ || cond == Condition.NE) {
+            if (a.getKind() == Kind.Object) {
+                condition = genObjectEquals(a, b);
+            } else {
+                condition = genIntegerEquals(a, b);
+            }
+        } else {
+            assert a.getKind() != Kind.Object && !cond.isUnsigned();
+            condition = genIntegerLessThan(a, b);
+        }
+        return condition;
+    }
+
+    private void genConstantTargetIf(BciBlock trueBlock, BciBlock falseBlock, boolean negate, LogicNode condition) {
+        LogicConstantNode constantLogicNode = (LogicConstantNode) condition;
+        boolean value = constantLogicNode.getValue();
+        if (negate) {
+            value = !value;
+        }
+        BciBlock nextBlock = falseBlock;
+        if (value) {
+            nextBlock = trueBlock;
+        }
+        appendGoto(nextBlock);
+    }
+
+    private int checkPositiveIntConstantPushed(BciBlock block) {
+        stream.setBCI(block.startBci);
+        int currentBC = stream.currentBC();
+        if (currentBC >= Bytecodes.ICONST_0 && currentBC <= Bytecodes.ICONST_5) {
+            int constValue = currentBC - Bytecodes.ICONST_0;
+            return constValue;
+        }
+        return -1;
+    }
+
+    private boolean gotoOrFallThroughAfterConstant(BciBlock block) {
+        stream.setBCI(block.startBci);
+        int currentBCI = stream.nextBCI();
+        stream.setBCI(currentBCI);
+        int currentBC = stream.currentBC();
+        return stream.currentBCI() > block.endBci || currentBC == Bytecodes.GOTO || currentBC == Bytecodes.GOTO_W;
+    }
+
+    private boolean returnAfterConstant(BciBlock block) {
+        stream.setBCI(block.startBci);
+        int currentBCI = stream.nextBCI();
+        stream.setBCI(currentBCI);
+        int currentBC = stream.currentBC();
+        return currentBC == Bytecodes.IRETURN;
+    }
+
+    public StampProvider getStampProvider() {
+        return stampProvider;
+    }
+
+    public MetaAccessProvider getMetaAccess() {
+        return metaAccess;
+    }
+
+    public void push(Kind slotKind, ValueNode value) {
+        assert value.isAlive();
+        frameState.push(slotKind, value);
+    }
+
+    private int getCurrentDimension() {
+        if (this.explodeLoopsContext == null || this.explodeLoopsContext.isEmpty()) {
+            return 0;
+        } else {
+            return this.explodeLoopsContext.peek().peelIteration;
+        }
+    }
+
+    public ConstantReflectionProvider getConstantReflection() {
+        return constantReflection;
+    }
+
+    /**
+     * Gets the graph being processed by this builder.
+     */
+    public StructuredGraph getGraph() {
+        return graph;
+    }
+
+    public BytecodeParser getParent() {
+        return parent;
+    }
+
+    public IntrinsicContext getIntrinsic() {
+        return intrinsicContext;
+    }
+
+    @Override
+    public String toString() {
+        Formatter fmt = new Formatter();
+        BytecodeParser bp = this;
+        String indent = "";
+        while (bp != null) {
+            if (bp != this) {
+                fmt.format("%n%s", indent);
+            }
+            fmt.format("%s [bci: %d, intrinsic: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingIntrinsic());
+            fmt.format("%n%s", new BytecodeDisassembler().disassemble(bp.method, bp.bci(), bp.bci() + 10));
+            bp = bp.parent;
+            indent += " ";
+        }
+        return fmt.toString();
+    }
+
+    public BailoutException bailout(String string) {
+        FrameState currentFrameState = createFrameState(bci(), null);
+        StackTraceElement[] elements = GraphUtil.approxSourceStackTraceElement(currentFrameState);
+        BailoutException bailout = new BailoutException(string);
+        throw GraphUtil.createBailoutException(string, bailout, elements);
+    }
+
+    private FrameState createFrameState(int bci, StateSplit forStateSplit) {
+        if (currentBlock != null && bci > currentBlock.endBci) {
+            frameState.clearNonLiveLocals(currentBlock, liveness, false);
+        }
+        return frameState.create(bci, forStateSplit);
+    }
+
+    public void setStateAfter(StateSplit sideEffect) {
+        assert sideEffect.hasSideEffect();
+        FrameState stateAfter = createFrameState(stream.nextBCI(), sideEffect);
+        sideEffect.setStateAfter(stateAfter);
+    }
+
+    private BytecodePosition createBytecodePosition() {
+        return frameState.createBytecodePosition(bci());
+    }
+
+    public void setCurrentFrameState(FrameStateBuilder frameState) {
+        this.frameState = frameState;
+    }
+
+    protected final BytecodeStream getStream() {
+        return stream;
+    }
+
+    public int bci() {
+        return stream.currentBCI();
+    }
+
+    public void loadLocal(int index, Kind kind) {
+        ValueNode value = frameState.loadLocal(index, kind);
+        frameState.push(kind, value);
+    }
+
+    public void storeLocal(Kind kind, int index) {
+        ValueNode value = frameState.pop(kind);
+        frameState.storeLocal(index, kind, value);
+    }
+
+    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).getJavaClass()));
+            } else {
+                handleUnresolvedLoadConstant(type);
+            }
+        } else if (con instanceof JavaConstant) {
+            JavaConstant constant = (JavaConstant) con;
+            frameState.push(constant.getKind(), appendConstant(constant));
+        } else {
+            throw new Error("lookupConstant returned an object of incorrect type");
+        }
+    }
+
+    private void genLoadIndexed(Kind kind) {
+        ValueNode index = frameState.pop(Kind.Int);
+        ValueNode array = emitExplicitExceptions(frameState.pop(Kind.Object), index);
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleLoadIndexed(this, array, index, kind)) {
+                return;
+            }
+        }
+
+        frameState.push(kind, append(genLoadIndexed(array, index, kind)));
+    }
+
+    private void genStoreIndexed(Kind kind) {
+        ValueNode value = frameState.pop(kind);
+        ValueNode index = frameState.pop(Kind.Int);
+        ValueNode array = emitExplicitExceptions(frameState.pop(Kind.Object), index);
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleStoreIndexed(this, array, index, kind, value)) {
+                return;
+            }
+        }
+
+        genStoreIndexed(array, index, kind, value);
+    }
+
+    private void genArithmeticOp(Kind kind, int opcode) {
+        ValueNode y = frameState.pop(kind);
+        ValueNode x = frameState.pop(kind);
+        ValueNode v;
+        switch (opcode) {
+            case IADD:
+            case LADD:
+                v = genIntegerAdd(x, y);
+                break;
+            case FADD:
+            case DADD:
+                v = genFloatAdd(x, y);
+                break;
+            case ISUB:
+            case LSUB:
+                v = genIntegerSub(x, y);
+                break;
+            case FSUB:
+            case DSUB:
+                v = genFloatSub(x, y);
+                break;
+            case IMUL:
+            case LMUL:
+                v = genIntegerMul(x, y);
+                break;
+            case FMUL:
+            case DMUL:
+                v = genFloatMul(x, y);
+                break;
+            case FDIV:
+            case DDIV:
+                v = genFloatDiv(x, y);
+                break;
+            case FREM:
+            case DREM:
+                v = genFloatRem(x, y);
+                break;
+            default:
+                throw shouldNotReachHere();
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genIntegerDivOp(Kind kind, int opcode) {
+        ValueNode y = frameState.pop(kind);
+        ValueNode x = frameState.pop(kind);
+        ValueNode v;
+        switch (opcode) {
+            case IDIV:
+            case LDIV:
+                v = genIntegerDiv(x, y);
+                break;
+            case IREM:
+            case LREM:
+                v = genIntegerRem(x, y);
+                break;
+            default:
+                throw shouldNotReachHere();
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genNegateOp(Kind kind) {
+        ValueNode x = frameState.pop(kind);
+        frameState.push(kind, append(genNegateOp(x)));
+    }
+
+    private void genShiftOp(Kind kind, int opcode) {
+        ValueNode s = frameState.pop(Kind.Int);
+        ValueNode x = frameState.pop(kind);
+        ValueNode v;
+        switch (opcode) {
+            case ISHL:
+            case LSHL:
+                v = genLeftShift(x, s);
+                break;
+            case ISHR:
+            case LSHR:
+                v = genRightShift(x, s);
+                break;
+            case IUSHR:
+            case LUSHR:
+                v = genUnsignedRightShift(x, s);
+                break;
+            default:
+                throw shouldNotReachHere();
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genLogicOp(Kind kind, int opcode) {
+        ValueNode y = frameState.pop(kind);
+        ValueNode x = frameState.pop(kind);
+        ValueNode v;
+        switch (opcode) {
+            case IAND:
+            case LAND:
+                v = genAnd(x, y);
+                break;
+            case IOR:
+            case LOR:
+                v = genOr(x, y);
+                break;
+            case IXOR:
+            case LXOR:
+                v = genXor(x, y);
+                break;
+            default:
+                throw shouldNotReachHere();
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genCompareOp(Kind kind, boolean isUnorderedLess) {
+        ValueNode y = frameState.pop(kind);
+        ValueNode x = frameState.pop(kind);
+        frameState.push(Kind.Int, append(genNormalizeCompare(x, y, isUnorderedLess)));
+    }
+
+    private void genFloatConvert(FloatConvert op, Kind from, Kind to) {
+        ValueNode input = frameState.pop(from);
+        frameState.push(to, append(genFloatConvert(op, input)));
+    }
+
+    private void genSignExtend(Kind from, Kind to) {
+        ValueNode input = frameState.pop(from);
+        if (from != from.getStackKind()) {
+            input = append(genNarrow(input, from.getBitCount()));
+        }
+        frameState.push(to, append(genSignExtend(input, to.getBitCount())));
+    }
+
+    private void genZeroExtend(Kind from, Kind to) {
+        ValueNode input = frameState.pop(from);
+        if (from != from.getStackKind()) {
+            input = append(genNarrow(input, from.getBitCount()));
+        }
+        frameState.push(to, append(genZeroExtend(input, to.getBitCount())));
+    }
+
+    private void genNarrow(Kind from, Kind to) {
+        ValueNode input = frameState.pop(from);
+        frameState.push(to, append(genNarrow(input, to.getBitCount())));
+    }
+
+    private void genIncrement() {
+        int index = getStream().readLocalIndex();
+        int delta = getStream().readIncrement();
+        ValueNode x = frameState.loadLocal(index, Kind.Int);
+        ValueNode y = appendConstant(JavaConstant.forInt(delta));
+        frameState.storeLocal(index, Kind.Int, append(genIntegerAdd(x, y)));
+    }
+
+    private void genIfZero(Condition cond) {
+        ValueNode y = appendConstant(JavaConstant.INT_0);
+        ValueNode x = frameState.pop(Kind.Int);
+        genIf(x, cond, y);
+    }
+
+    private void genIfNull(Condition cond) {
+        ValueNode y = appendConstant(JavaConstant.NULL_POINTER);
+        ValueNode x = frameState.pop(Kind.Object);
+        genIf(x, cond, y);
+    }
+
+    private void genIfSame(Kind kind, Condition cond) {
+        ValueNode y = frameState.pop(kind);
+        ValueNode x = frameState.pop(kind);
+        genIf(x, cond, y);
+    }
+
+    protected JavaType lookupType(int cpi, int bytecode) {
+        maybeEagerlyResolve(cpi, bytecode);
+        JavaType result = constantPool.lookupType(cpi, bytecode);
+        assert !graphBuilderConfig.unresolvedIsError() || result instanceof ResolvedJavaType;
+        return result;
+    }
+
+    private JavaMethod lookupMethod(int cpi, int opcode) {
+        maybeEagerlyResolve(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) {
+        maybeEagerlyResolve(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) {
+        maybeEagerlyResolve(cpi, opcode);
+        Object result = constantPool.lookupConstant(cpi);
+        assert !graphBuilderConfig.eagerResolving() || !(result instanceof JavaType) || (result instanceof ResolvedJavaType) : result;
+        return result;
+    }
+
+    private void maybeEagerlyResolve(int cpi, int bytecode) {
+        if (graphBuilderConfig.eagerResolving() || intrinsicContext != null) {
+            constantPool.loadReferencedType(cpi, bytecode);
+        }
+    }
+
+    private JavaTypeProfile getProfileForTypeCheck(ResolvedJavaType type) {
+        if (parsingIntrinsic() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) {
+            return null;
+        } else {
+            return profilingInfo.getTypeProfile(bci());
+        }
+    }
+
+    private void genCheckCast() {
+        int cpi = getStream().readCPI();
+        JavaType type = lookupType(cpi, CHECKCAST);
+        ValueNode object = frameState.pop(Kind.Object);
+
+        if (!(type instanceof ResolvedJavaType)) {
+            handleUnresolvedCheckCast(type, object);
+            return;
+        }
+        ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+        JavaTypeProfile profile = getProfileForTypeCheck(resolvedType);
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleCheckCast(this, object, resolvedType, profile)) {
+                return;
+            }
+        }
+
+        ValueNode checkCastNode = null;
+        if (profile != null) {
+            if (profile.getNullSeen().isFalse()) {
+                object = append(GuardingPiNode.createNullCheck(object));
+                ResolvedJavaType singleType = profile.asSingleType();
+                if (singleType != null) {
+                    LogicNode typeCheck = append(TypeCheckNode.create(singleType, object));
+                    if (typeCheck.isTautology()) {
+                        checkCastNode = object;
+                    } else {
+                        GuardingPiNode piNode = append(new GuardingPiNode(object, typeCheck, false, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile,
+                                        StampFactory.exactNonNull(singleType)));
+                        checkCastNode = piNode;
+                    }
+                }
+            }
+        }
+        if (checkCastNode == null) {
+            checkCastNode = append(createCheckCast(resolvedType, object, profile, false));
+        }
+        frameState.push(Kind.Object, checkCastNode);
+    }
+
+    private void genInstanceOf() {
+        int cpi = getStream().readCPI();
+        JavaType type = lookupType(cpi, INSTANCEOF);
+        ValueNode object = frameState.pop(Kind.Object);
+
+        if (!(type instanceof ResolvedJavaType)) {
+            handleUnresolvedInstanceOf(type, object);
+            return;
+        }
+        ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+        JavaTypeProfile profile = getProfileForTypeCheck(resolvedType);
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleInstanceOf(this, object, resolvedType, profile)) {
+                return;
+            }
+        }
+
+        ValueNode instanceOfNode = null;
+        if (profile != null) {
+            if (profile.getNullSeen().isFalse()) {
+                object = append(GuardingPiNode.createNullCheck(object));
+                ResolvedJavaType singleType = profile.asSingleType();
+                if (singleType != null) {
+                    LogicNode typeCheck = append(TypeCheckNode.create(singleType, object));
+                    append(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile));
+                    instanceOfNode = LogicConstantNode.forBoolean(resolvedType.isAssignableFrom(singleType));
+                }
+            }
+        }
+        if (instanceOfNode == null) {
+            instanceOfNode = createInstanceOf(resolvedType, object, profile);
+        }
+        frameState.push(Kind.Int, append(genConditional(genUnique(instanceOfNode))));
+    }
+
+    void genNewInstance(int cpi) {
+        JavaType type = lookupType(cpi, NEW);
+
+        if (!(type instanceof ResolvedJavaType) || !((ResolvedJavaType) type).isInitialized()) {
+            handleUnresolvedNewInstance(type);
+            return;
+        }
+        ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+
+        ResolvedJavaType[] skippedExceptionTypes = this.graphBuilderConfig.getSkippedExceptionTypes();
+        if (skippedExceptionTypes != null) {
+            for (ResolvedJavaType exceptionType : skippedExceptionTypes) {
+                if (exceptionType.isAssignableFrom(resolvedType)) {
+                    append(new DeoptimizeNode(DeoptimizationAction.None, TransferToInterpreter));
+                    return;
+                }
+            }
+        }
+
+        frameState.push(Kind.Object, append(createNewInstance(resolvedType, true)));
+    }
+
+    /**
+     * 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
+     */
+    private static Class<?> arrayTypeCodeToClass(int code) {
+        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);
+        }
+    }
+
+    private void genNewPrimitiveArray(int typeCode) {
+        ResolvedJavaType elementType = metaAccess.lookupJavaType(arrayTypeCodeToClass(typeCode));
+        ValueNode length = frameState.pop(Kind.Int);
+        frameState.push(Kind.Object, append(createNewArray(elementType, length, true)));
+    }
+
+    private void genNewObjectArray(int cpi) {
+        JavaType type = lookupType(cpi, ANEWARRAY);
+        ValueNode length = frameState.pop(Kind.Int);
+
+        if (!(type instanceof ResolvedJavaType)) {
+            handleUnresolvedNewObjectArray(type, length);
+            return;
+        }
+        ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+
+        frameState.push(Kind.Object, append(createNewArray(resolvedType, length, true)));
+    }
+
+    private void genNewMultiArray(int cpi) {
+        JavaType type = lookupType(cpi, MULTIANEWARRAY);
+        int rank = getStream().readUByte(bci() + 3);
+        List<ValueNode> dims = new ArrayList<>(Collections.nCopies(rank, null));
+        for (int i = rank - 1; i >= 0; i--) {
+            dims.set(i, frameState.pop(Kind.Int));
+        }
+
+        if (!(type instanceof ResolvedJavaType)) {
+            handleUnresolvedNewMultiArray(type, dims);
+            return;
+        }
+        ResolvedJavaType resolvedType = (ResolvedJavaType) type;
+
+        frameState.push(Kind.Object, append(createNewMultiArray(resolvedType, dims)));
+    }
+
+    private void genGetField(JavaField field) {
+        ValueNode receiver = emitExplicitExceptions(frameState.pop(Kind.Object), null);
+
+        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
+            handleUnresolvedLoadField(field, receiver);
+            return;
+        }
+        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleLoadField(this, receiver, resolvedField)) {
+                return;
+            }
+        }
+
+        frameState.push(field.getKind(), append(genLoadField(receiver, resolvedField)));
+    }
+
+    /**
+     * @param receiver the receiver of an object based operation
+     * @param index the index of an array based operation that is to be tested for out of bounds.
+     *            This is null for a non-array operation.
+     * @return the receiver value possibly modified to have a tighter stamp
+     */
+    protected ValueNode emitExplicitExceptions(ValueNode receiver, ValueNode index) {
+        assert receiver != null;
+        if (graphBuilderConfig.omitAllExceptionEdges() || profilingInfo == null ||
+                        (optimisticOpts.useExceptionProbabilityForOperations() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE && !GraalOptions.StressExplicitExceptionCode.getValue())) {
+            return receiver;
+        }
+
+        ValueNode nonNullReceiver = emitExplicitNullCheck(receiver);
+        if (index != null) {
+            ValueNode length = append(genArrayLength(nonNullReceiver));
+            emitExplicitBoundsCheck(index, length);
+        }
+        EXPLICIT_EXCEPTIONS.increment();
+        return nonNullReceiver;
+    }
+
+    private void genPutField(JavaField field) {
+        ValueNode value = frameState.pop(field.getKind());
+        ValueNode receiver = emitExplicitExceptions(frameState.pop(Kind.Object), null);
+
+        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
+            handleUnresolvedStoreField(field, value, receiver);
+            return;
+        }
+        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleStoreField(this, receiver, resolvedField, value)) {
+                return;
+            }
+        }
+
+        genStoreField(receiver, resolvedField, value);
+    }
+
+    private void genGetStatic(JavaField field) {
+        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
+            handleUnresolvedLoadField(field, null);
+            return;
+        }
+        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
+
+        /*
+         * Javac does not allow use of "$assertionsDisabled" for a field name but Eclipse does, in
+         * which case a suffix is added to the generated field.
+         */
+        if ((parsingIntrinsic() || graphBuilderConfig.omitAssertions()) && resolvedField.isSynthetic() && resolvedField.getName().startsWith("$assertionsDisabled")) {
+            frameState.push(field.getKind(), ConstantNode.forBoolean(true, graph));
+            return;
+        }
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleLoadStaticField(this, resolvedField)) {
+                return;
+            }
+        }
+
+        frameState.push(field.getKind(), append(genLoadField(null, resolvedField)));
+    }
+
+    private void genPutStatic(JavaField field) {
+        ValueNode value = frameState.pop(field.getKind());
+        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
+            handleUnresolvedStoreField(field, value, null);
+            return;
+        }
+        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
+
+        for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
+            if (plugin.handleStoreStaticField(this, resolvedField, value)) {
+                return;
+            }
+        }
+
+        genStoreField(null, resolvedField, value);
+    }
+
+    private double[] switchProbability(int numberOfCases, int bci) {
+        double[] prob = (profilingInfo == null ? null : 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;
+    }
+
+    static class SuccessorInfo {
+        final int blockIndex;
+        int actualIndex;
+
+        public SuccessorInfo(int blockSuccessorIndex) {
+            this.blockIndex = blockSuccessorIndex;
+            actualIndex = -1;
+        }
+    }
+
+    private void genSwitch(BytecodeSwitch bs) {
+        int bci = bci();
+        ValueNode value = frameState.pop(Kind.Int);
+
+        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;
+        boolean constantValue = value.isConstant();
+        for (int i = 0; i < nofCases + 1; i++) {
+            if (i < nofCases) {
+                keys[i] = bs.keyAt(i);
+            }
+
+            if (!constantValue && 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;
+            }
+        }
+
+        genIntegerSwitch(value, actualSuccessors, keys, keyProbabilities, keySuccessors);
+
+    }
+
+    protected boolean isNeverExecutedCode(double probability) {
+        return probability == 0 && optimisticOpts.removeNeverExecutedCode();
+    }
+
+    protected double branchProbability() {
+        if (profilingInfo == null) {
+            return 0.5;
+        }
+        assert assertAtIfBytecode();
+        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;
+            }
+        }
+        return probability;
+    }
+
+    private boolean assertAtIfBytecode() {
+        int bytecode = stream.currentBC();
+        switch (bytecode) {
+            case IFEQ:
+            case IFNE:
+            case IFLT:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case IF_ICMPEQ:
+            case IF_ICMPNE:
+            case IF_ICMPLT:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ACMPEQ:
+            case IF_ACMPNE:
+            case IFNULL:
+            case IFNONNULL:
+                return true;
+        }
+        assert false : String.format("%x is not an if bytecode", bytecode);
+        return true;
+    }
+
+    public final void processBytecode(int bci, int opcode) {
+        int cpi;
+
+        // @formatter:off
+        // Checkstyle: stop
+        switch (opcode) {
+            case NOP            : /* nothing to do */ break;
+            case ACONST_NULL    : frameState.push(Kind.Object, appendConstant(JavaConstant.NULL_POINTER)); break;
+            case ICONST_M1      : // fall through
+            case ICONST_0       : // fall through
+            case ICONST_1       : // fall through
+            case ICONST_2       : // fall through
+            case ICONST_3       : // fall through
+            case ICONST_4       : // fall through
+            case ICONST_5       : frameState.push(Kind.Int, appendConstant(JavaConstant.forInt(opcode - ICONST_0))); break;
+            case LCONST_0       : // fall through
+            case LCONST_1       : frameState.push(Kind.Long, appendConstant(JavaConstant.forLong(opcode - LCONST_0))); break;
+            case FCONST_0       : // fall through
+            case FCONST_1       : // fall through
+            case FCONST_2       : frameState.push(Kind.Float, appendConstant(JavaConstant.forFloat(opcode - FCONST_0))); break;
+            case DCONST_0       : // fall through
+            case DCONST_1       : frameState.push(Kind.Double, appendConstant(JavaConstant.forDouble(opcode - DCONST_0))); break;
+            case BIPUSH         : frameState.push(Kind.Int, appendConstant(JavaConstant.forInt(stream.readByte()))); break;
+            case SIPUSH         : frameState.push(Kind.Int, appendConstant(JavaConstant.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           : frameState.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(getStream(), bci())); break;
+            case IRETURN        : genReturn(frameState.pop(Kind.Int), Kind.Int); break;
+            case LRETURN        : genReturn(frameState.pop(Kind.Long), Kind.Long); break;
+            case FRETURN        : genReturn(frameState.pop(Kind.Float), Kind.Float); break;
+            case DRETURN        : genReturn(frameState.pop(Kind.Double), Kind.Double); break;
+            case ARETURN        : genReturn(frameState.pop(Kind.Object), Kind.Object); break;
+            case RETURN         : genReturn(null, Kind.Void); 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.pop(Kind.Object), stream.nextBCI()); break;
+            case MONITOREXIT    : genMonitorExit(frameState.pop(Kind.Object), null, stream.nextBCI()); 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 %d (%s) [bci=%d]", opcode, nameOf(opcode), bci);
+        }
+        // @formatter:on
+        // Checkstyle: resume
+    }
+
+    private void genArrayLength() {
+        frameState.push(Kind.Int, append(genArrayLength(frameState.pop(Kind.Object))));
+    }
+
+    public ResolvedJavaMethod getMethod() {
+        return method;
+    }
+
+    public FrameStateBuilder getFrameStateBuilder() {
+        return frameState;
+    }
+
+    protected boolean traceInstruction(int bci, int opcode, boolean blockStart) {
+        if (Debug.isEnabled() && Options.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) {
+            traceInstructionHelper(bci, opcode, blockStart);
+        }
+        return true;
+    }
+
+    private void traceInstructionHelper(int bci, int opcode, boolean blockStart) {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append(blockStart ? '+' : '|');
+        if (bci < 10) {
+            sb.append("  ");
+        } else if (bci < 100) {
+            sb.append(' ');
+        }
+        sb.append(bci).append(": ").append(Bytecodes.nameOf(opcode));
+        for (int i = bci + 1; i < stream.nextBCI(); ++i) {
+            sb.append(' ').append(stream.readUByte(i));
+        }
+        if (!currentBlock.getJsrScope().isEmpty()) {
+            sb.append(' ').append(currentBlock.getJsrScope());
+        }
+        Debug.log("%s", sb);
+    }
+
+    public boolean parsingIntrinsic() {
+        return intrinsicContext != null;
+    }
+
+    public BytecodeParser getNonIntrinsicAncestor() {
+        BytecodeParser ancestor = parent;
+        while (ancestor != null && ancestor.parsingIntrinsic()) {
+            ancestor = ancestor.parent;
+        }
+        return ancestor;
+    }
+
+    static String nSpaces(int n) {
+        return n == 0 ? "" : format("%" + n + "s", "");
+    }
+
+    @SuppressWarnings("all")
+    private static boolean assertionsEnabled() {
+        boolean assertionsEnabled = false;
+        assert assertionsEnabled = true;
+        return assertionsEnabled;
+    }
+}
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri May 29 19:11:39 2015 -0700
@@ -24,7 +24,7 @@
 
 import static com.oracle.graal.bytecode.Bytecodes.*;
 import static com.oracle.graal.graph.iterators.NodePredicates.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
 import static com.oracle.jvmci.common.JVMCIError.*;
 
 import java.util.*;
@@ -33,7 +33,6 @@
 import com.oracle.graal.graphbuilderconf.IntrinsicContext.SideEffectsState;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
-import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri May 29 19:11:39 2015 -0700
@@ -22,177 +22,21 @@
  */
 package com.oracle.graal.java;
 
-import static com.oracle.graal.bytecode.Bytecodes.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.compiler.common.type.StampFactory.*;
-import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
-import static com.oracle.graal.nodes.StructuredGraph.*;
-import static com.oracle.graal.nodes.type.StampTool.*;
-import static com.oracle.jvmci.code.TypeCheckHints.*;
-import static com.oracle.jvmci.common.JVMCIError.*;
-import static com.oracle.jvmci.meta.DeoptimizationAction.*;
-import static com.oracle.jvmci.meta.DeoptimizationReason.*;
-import static java.lang.String.*;
-
-import java.util.*;
 
-import com.oracle.graal.bytecode.*;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.calc.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.graph.Graph.Mark;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.Node.ValueNumberable;
-import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
-import com.oracle.graal.java.BciBlockMapping.BciBlock;
-import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
-import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
-import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.jvmci.code.*;
-import com.oracle.jvmci.common.*;
 import com.oracle.jvmci.debug.*;
-import com.oracle.jvmci.debug.Debug.Scope;
 import com.oracle.jvmci.meta.*;
-import com.oracle.jvmci.options.*;
 
 /**
- * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph.
+ * Parses the bytecodes of a method and builds the IR graph.
  */
 public class GraphBuilderPhase extends BasePhase<HighTierContext> {
 
-    public static class Options {
-        // @formatter:off
-        @Option(help = "The trace level for the bytecode parser used when building a graph from bytecode", type = OptionType.Debug)
-        public static final OptionValue<Integer> TraceBytecodeParserLevel = new OptionValue<>(0);
-
-        @Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert)
-        public static final StableOptionValue<Boolean> InlineDuringParsing = new StableOptionValue<>(false);
-
-        @Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert)
-        public static final StableOptionValue<Boolean> InlineIntrinsicsDuringParsing = new StableOptionValue<>(true);
-
-        @Option(help = "Traces inlining performed during bytecode parsing.", type = OptionType.Debug)
-        public static final StableOptionValue<Boolean> TraceInlineDuringParsing = new StableOptionValue<>(false);
-
-        @Option(help = "Traces use of plugins during bytecode parsing.", type = OptionType.Debug)
-        public static final StableOptionValue<Boolean> TraceParserPlugins = new StableOptionValue<>(false);
-
-        @Option(help = "Maximum depth when inlining during bytecode parsing.", type = OptionType.Debug)
-        public static final StableOptionValue<Integer> InlineDuringParsingMaxDepth = new StableOptionValue<>(10);
-
-        @Option(help = "Dump graphs after non-trivial changes during bytecode parsing.", type = OptionType.Debug)
-        public static final StableOptionValue<Boolean> DumpDuringGraphBuilding = new StableOptionValue<>(false);
-
-        @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)
-        public static final OptionValue<Integer> MaximumLoopExplosionCount = new OptionValue<>(10000);
-
-        @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug)
-        public static final OptionValue<Boolean> FailedLoopExplosionIsFatal = new OptionValue<>(false);
-
-        @Option(help = "When creating info points hide the methods of the substitutions.", type = OptionType.Debug)
-        public static final OptionValue<Boolean> HideSubstitutionStates = new OptionValue<>(false);
-
-        // @formatter:on
-    }
-
-    /**
-     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
-     * bytecode instructions as they are parsed.
-     */
-    public static final int TRACELEVEL_INSTRUCTIONS = 1;
-
-    /**
-     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
-     * frame state before each bytecode instruction as it is parsed.
-     */
-    public static final int TRACELEVEL_STATE = 2;
-
-    /**
-     * Meters the number of actual bytecodes parsed.
-     */
-    public static final DebugMetric BytecodesParsed = Debug.metric("BytecodesParsed");
-
-    /**
-     * 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
-    }
-
-    protected static final DebugMetric EXPLICIT_EXCEPTIONS = Debug.metric("ExplicitExceptions");
-
-    protected 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.
-     */
-    public 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;
-    }
-
-    static class SuccessorInfo {
-
-        int blockIndex;
-        int actualIndex;
-
-        public SuccessorInfo(int blockSuccessorIndex) {
-            this.blockIndex = blockSuccessorIndex;
-            actualIndex = -1;
-        }
-    }
-
     private final GraphBuilderConfiguration graphBuilderConfig;
 
     public GraphBuilderPhase(GraphBuilderConfiguration config) {
@@ -208,119 +52,16 @@
         return graphBuilderConfig;
     }
 
-    /**
-     * A scoped object for tasks to be performed after parsing an intrinsic such as processing
-     * {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frames states.
-     */
-    static class IntrinsicScope implements AutoCloseable {
-        FrameState stateBefore;
-        final Mark mark;
-        final BytecodeParser parser;
-
-        /**
-         * Creates a scope for root parsing an intrinsic.
-         *
-         * @param parser the parsing context of the intrinsic
-         */
-        public IntrinsicScope(BytecodeParser parser) {
-            this.parser = parser;
-            assert parser.parent == null;
-            assert parser.bci() == 0;
-            mark = null;
-        }
-
-        /**
-         * Creates a scope for parsing an intrinsic during graph builder inlining.
-         *
-         * @param parser the parsing context of the (non-intrinsic) method calling the intrinsic
-         * @param args the arguments to the call
-         */
-        public IntrinsicScope(BytecodeParser parser, Kind[] argSlotKinds, ValueNode[] args) {
-            assert !parser.parsingIntrinsic();
-            this.parser = parser;
-            mark = parser.getGraph().getMark();
-            stateBefore = parser.frameState.create(parser.bci(), parser.getNonIntrinsicAncestor(), false, argSlotKinds, args);
-        }
-
-        public void close() {
-            IntrinsicContext intrinsic = parser.intrinsicContext;
-            if (intrinsic != null && intrinsic.isPostParseInlined()) {
-                return;
-            }
-
-            processPlaceholderFrameStates(intrinsic);
-        }
-
-        /**
-         * Fixes up the {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frame states
-         * added to the graph while parsing/inlining the intrinsic for which this object exists.
-         */
-        private void processPlaceholderFrameStates(IntrinsicContext intrinsic) {
-            FrameState stateAfterReturn = null;
-            StructuredGraph graph = parser.getGraph();
-            for (Node node : graph.getNewNodes(mark)) {
-                if (node instanceof FrameState) {
-                    FrameState frameState = (FrameState) node;
-                    if (BytecodeFrame.isPlaceholderBci(frameState.bci)) {
-                        if (frameState.bci == BytecodeFrame.AFTER_BCI) {
-                            FrameStateBuilder frameStateBuilder = parser.frameState;
-                            if (frameState.stackSize() != 0) {
-                                assert frameState.usages().count() == 1;
-                                ValueNode returnVal = frameState.stackAt(0);
-                                assert returnVal == frameState.usages().first();
-
-                                /*
-                                 * Swap the top-of-stack value with the side-effect return value
-                                 * using the frame state.
-                                 */
-                                Kind returnKind = parser.currentInvokeReturnType.getKind();
-                                ValueNode tos = frameStateBuilder.pop(returnKind);
-                                assert tos.getKind() == returnVal.getKind();
-                                FrameState newFrameState = frameStateBuilder.create(parser.stream.nextBCI(), parser.getNonIntrinsicAncestor(), false, new Kind[]{returnKind},
-                                                new ValueNode[]{returnVal});
-                                frameState.replaceAndDelete(newFrameState);
-                                frameStateBuilder.push(returnKind, tos);
-                            } else {
-                                if (stateAfterReturn == null) {
-                                    if (intrinsic != null) {
-                                        assert intrinsic.isCompilationRoot();
-                                        stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI));
-                                    } else {
-                                        stateAfterReturn = frameStateBuilder.create(parser.stream.nextBCI(), null);
-                                    }
-                                }
-                                frameState.replaceAndDelete(stateAfterReturn);
-                            }
-                        } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) {
-                            if (stateBefore == null) {
-                                stateBefore = graph.start().stateAfter();
-                            }
-                            if (stateBefore != frameState) {
-                                frameState.replaceAndDelete(stateBefore);
-                            }
-                        } else {
-                            assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     // Fully qualified name is a workaround for JDK-8056066
     public static class Instance extends com.oracle.graal.phases.Phase {
 
-        protected StructuredGraph graph;
-
-        private final MetaAccessProvider metaAccess;
-
+        protected final MetaAccessProvider metaAccess;
+        protected final StampProvider stampProvider;
+        protected final ConstantReflectionProvider constantReflection;
+        protected final GraphBuilderConfiguration graphBuilderConfig;
+        protected final OptimisticOptimizations optimisticOpts;
         private final IntrinsicContext initialIntrinsicContext;
 
-        private final GraphBuilderConfiguration graphBuilderConfig;
-        private final OptimisticOptimizations optimisticOpts;
-        private final StampProvider stampProvider;
-        private final ConstantReflectionProvider constantReflection;
-
         public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig,
                         OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
             this.graphBuilderConfig = graphBuilderConfig;
@@ -329,3292 +70,21 @@
             this.stampProvider = stampProvider;
             this.constantReflection = constantReflection;
             this.initialIntrinsicContext = initialIntrinsicContext;
-
-            assert metaAccess != null;
         }
 
         @Override
-        protected void run(@SuppressWarnings("hiding") StructuredGraph graph) {
-            ResolvedJavaMethod method = graph.method();
-            int entryBCI = graph.getEntryBCI();
-            assert method.getCode() != null : "method must contain bytecodes: " + method;
-            this.graph = graph;
-            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method);
+        protected void run(StructuredGraph graph) {
+            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), graph.method());
             try {
-                IntrinsicContext intrinsicContext = initialIntrinsicContext;
-                BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, intrinsicContext);
-                FrameStateBuilder frameState = new FrameStateBuilder(parser, method, graph);
-
-                frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugins());
-
-                try (IntrinsicScope s = intrinsicContext != null ? new IntrinsicScope(parser) : null) {
-                    parser.build(graph.start(), frameState);
-                }
-                GraphUtil.normalizeLoops(graph);
-
-                // Remove dead parameters.
-                for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
-                    if (param.hasNoUsages()) {
-                        assert param.inputs().isEmpty();
-                        param.safeDelete();
-                    }
-                }
-
-                // Remove redundant begin nodes.
-                Debug.dump(graph, "Before removing redundant begins");
-                for (BeginNode beginNode : graph.getNodes(BeginNode.TYPE)) {
-                    Node predecessor = beginNode.predecessor();
-                    if (predecessor instanceof ControlSplitNode) {
-                        // The begin node is necessary.
-                    } else {
-                        if (beginNode.hasUsages()) {
-                            reanchorGuardedNodes(beginNode);
-                        }
-                        GraphUtil.unlinkFixedNode(beginNode);
-                        beginNode.safeDelete();
-                    }
-                }
+                createBytecodeParser(graph, null, graph.method(), graph.getEntryBCI(), initialIntrinsicContext).buildRootMethod();
             } finally {
                 filter.remove();
             }
-
-            ComputeLoopFrequenciesClosure.compute(graph);
-        }
-
-        /**
-         * Removes {@link GuardedNode}s from {@code beginNode}'s usages and re-attaches them to an
-         * appropriate preceeding {@link GuardingNode}.
-         */
-        protected void reanchorGuardedNodes(BeginNode beginNode) {
-            // Find the new guarding node
-            GuardingNode guarding = null;
-            Node pred = beginNode.predecessor();
-            while (pred != null) {
-                if (pred instanceof BeginNode) {
-                    if (pred.predecessor() instanceof ControlSplitNode) {
-                        guarding = (GuardingNode) pred;
-                        break;
-                    }
-                } else if (pred.getNodeClass().getAllowedUsageTypes().contains(InputType.Guard)) {
-                    guarding = (GuardingNode) pred;
-                    break;
-                }
-                pred = pred.predecessor();
-            }
-
-            // Reset the guard for all of beginNode's usages
-            for (Node usage : beginNode.usages().snapshot()) {
-                GuardedNode guarded = (GuardedNode) usage;
-                assert guarded.getGuard() == beginNode;
-                guarded.setGuard(guarding);
-            }
-            assert beginNode.hasNoUsages() : beginNode;
-        }
-
-        @Override
-        protected String getDetailedName() {
-            return getName() + " " + graph.method().format("%H.%n(%p):%r");
-        }
-
-        private static class Target {
-
-            FixedNode fixed;
-            FrameStateBuilder state;
-
-            public Target(FixedNode fixed, FrameStateBuilder state) {
-                this.fixed = fixed;
-                this.state = state;
-            }
-        }
-
-        private static class ExplodedLoopContext {
-            private BciBlock header;
-            private int[] targetPeelIteration;
-            private int peelIteration;
-        }
-
-        @SuppressWarnings("serial")
-        public class BytecodeParserError extends JVMCIError {
-
-            public BytecodeParserError(Throwable cause) {
-                super(cause);
-            }
-
-            public BytecodeParserError(String msg, Object... args) {
-                super(msg, args);
-            }
         }
 
-        public class BytecodeParser implements GraphBuilderContext {
-
-            private BciBlockMapping blockMap;
-            private LocalLiveness liveness;
-            protected final int entryBCI;
-            private final BytecodeParser parent;
-
-            private LineNumberTable lnt;
-            private int previousLineNumber;
-            private int currentLineNumber;
-
-            private ValueNode methodSynchronizedObject;
-
-            private ValueNode returnValue;
-            private FixedWithNextNode beforeReturnNode;
-            private ValueNode unwindValue;
-            private FixedWithNextNode beforeUnwindNode;
-
-            private FixedWithNextNode lastInstr;                 // the last instruction added
-            private final boolean explodeLoops;
-            private final boolean mergeExplosions;
-            private final Map<FrameStateBuilder, Integer> mergeExplosionsMap;
-            private Deque<ExplodedLoopContext> explodeLoopsContext;
-            private int nextPeelIteration = 1;
-            private boolean controlFlowSplit;
-            private final InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(this);
-
-            private FixedWithNextNode[] firstInstructionArray;
-            private FrameStateBuilder[] entryStateArray;
-            private FixedWithNextNode[][] firstInstructionMatrix;
-            private FrameStateBuilder[][] entryStateMatrix;
-
-            public BytecodeParser(BytecodeParser parent, MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig,
-                            OptimisticOptimizations optimisticOpts, int entryBCI, IntrinsicContext intrinsicContext) {
-                this.graphBuilderConfig = graphBuilderConfig;
-                this.optimisticOpts = optimisticOpts;
-                this.metaAccess = metaAccess;
-                this.stream = new BytecodeStream(method.getCode());
-                this.profilingInfo = (graphBuilderConfig.getUseProfiling() ? method.getProfilingInfo() : null);
-                this.constantPool = method.getConstantPool();
-                this.method = method;
-                this.intrinsicContext = intrinsicContext;
-                assert metaAccess != null;
-                this.entryBCI = entryBCI;
-                this.parent = parent;
-
-                if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
-                    lnt = method.getLineNumberTable();
-                    previousLineNumber = -1;
-                }
-
-                LoopExplosionPlugin loopExplosionPlugin = graphBuilderConfig.getPlugins().getLoopExplosionPlugin();
-                if (loopExplosionPlugin != null) {
-                    explodeLoops = loopExplosionPlugin.shouldExplodeLoops(method);
-                    if (explodeLoops) {
-                        mergeExplosions = loopExplosionPlugin.shouldMergeExplosions(method);
-                        mergeExplosionsMap = new HashMap<>();
-                    } else {
-                        mergeExplosions = false;
-                        mergeExplosionsMap = null;
-                    }
-                } else {
-                    explodeLoops = false;
-                    mergeExplosions = false;
-                    mergeExplosionsMap = null;
-                }
-            }
-
-            public ValueNode getReturnValue() {
-                return returnValue;
-            }
-
-            public FixedWithNextNode getBeforeReturnNode() {
-                return this.beforeReturnNode;
-            }
-
-            public ValueNode getUnwindValue() {
-                return unwindValue;
-            }
-
-            public FixedWithNextNode getBeforeUnwindNode() {
-                return this.beforeUnwindNode;
-            }
-
-            protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) {
-                if (PrintProfilingInformation.getValue() && profilingInfo != null) {
-                    TTY.println("Profiling info for " + method.format("%H.%n(%p)"));
-                    TTY.println(MetaUtil.indent(profilingInfo.toString(method, CodeUtil.NEW_LINE), "  "));
-                }
-
-                try (Indent indent = Debug.logAndIndent("build graph for %s", method)) {
-
-                    // compute the block map, setup exception handlers and get the entrypoint(s)
-                    BciBlockMapping newMapping = BciBlockMapping.create(stream, method);
-                    this.blockMap = newMapping;
-                    this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()];
-                    this.entryStateArray = new FrameStateBuilder[blockMap.getBlockCount()];
-
-                    try (Scope s = Debug.scope("LivenessAnalysis")) {
-                        int maxLocals = method.getMaxLocals();
-                        liveness = LocalLiveness.compute(stream, blockMap.getBlocks(), maxLocals, blockMap.getLoopCount());
-                    } catch (Throwable e) {
-                        throw Debug.handle(e);
-                    }
-
-                    lastInstr = startInstruction;
-                    this.setCurrentFrameState(startFrameState);
-                    stream.setBCI(0);
-
-                    BciBlock startBlock = blockMap.getStartBlock();
-                    if (startInstruction == graph.start()) {
-                        StartNode startNode = graph.start();
-                        if (method.isSynchronized()) {
-                            assert !parsingIntrinsic();
-                            startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI, startNode));
-                        } else {
-                            if (!parsingIntrinsic()) {
-                                if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
-                                    /*
-                                     * Don't clear the receiver when Object.<init> is the
-                                     * compilation root. The receiver is needed as input to
-                                     * RegisterFinalizerNode.
-                                     */
-                                } else {
-                                    frameState.clearNonLiveLocals(startBlock, liveness, true);
-                                }
-                                assert bci() == 0;
-                                startNode.setStateAfter(createFrameState(bci(), startNode));
-                            } else {
-                                if (startNode.stateAfter() == null) {
-                                    FrameState stateAfterStart = createStateAfterStartOfReplacementGraph();
-                                    startNode.setStateAfter(stateAfterStart);
-                                }
-                            }
-                        }
-                    }
-
-                    if (method.isSynchronized()) {
-                        // add a monitor enter to the start block
-                        methodSynchronizedObject = synchronizedObject(frameState, method);
-                        frameState.clearNonLiveLocals(startBlock, liveness, true);
-                        assert bci() == 0;
-                        genMonitorEnter(methodSynchronizedObject, bci());
-                    }
-
-                    if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
-                        append(createInfoPointNode(InfopointReason.METHOD_START));
-                    }
-
-                    currentBlock = blockMap.getStartBlock();
-                    setEntryState(startBlock, 0, frameState);
-                    if (startBlock.isLoopHeader && !explodeLoops) {
-                        appendGoto(startBlock);
-                    } else {
-                        setFirstInstruction(startBlock, 0, lastInstr);
-                    }
-
-                    int index = 0;
-                    BciBlock[] blocks = blockMap.getBlocks();
-                    while (index < blocks.length) {
-                        BciBlock block = blocks[index];
-                        index = iterateBlock(blocks, block);
-                    }
-
-                    if (this.mergeExplosions) {
-                        Debug.dump(graph, "Before loop detection");
-                        detectLoops(startInstruction);
-                    }
-
-                    if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue() && this.beforeReturnNode != startInstruction) {
-                        Debug.dump(graph, "Bytecodes parsed: " + method.getDeclaringClass().getUnqualifiedName() + "." + method.getName());
-                    }
-                }
-            }
-
-            /**
-             * Creates the frame state after the start node of a graph for an
-             * {@link IntrinsicContext intrinsic} that is the parse root (either for root compiling
-             * or for post-parse inlining).
-             */
-            private FrameState createStateAfterStartOfReplacementGraph() {
-                assert parent == null;
-                assert frameState.getMethod().equals(intrinsicContext.getIntrinsicMethod());
-                assert bci() == 0;
-                assert frameState.stackSize() == 0;
-                FrameState stateAfterStart;
-                if (intrinsicContext.isPostParseInlined()) {
-                    stateAfterStart = graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
-                } else {
-                    ResolvedJavaMethod original = intrinsicContext.getOriginalMethod();
-                    ValueNode[] locals;
-                    if (original.getMaxLocals() == frameState.localsSize() || original.isNative()) {
-                        locals = frameState.locals;
-                    } else {
-                        locals = new ValueNode[original.getMaxLocals()];
-                        int parameterCount = original.getSignature().getParameterCount(!original.isStatic());
-                        for (int i = 0; i < parameterCount; i++) {
-                            ValueNode param = frameState.locals[i];
-                            locals[i] = param;
-                            assert param == null || param instanceof ParameterNode || param.isConstant();
-                        }
-                    }
-                    ValueNode[] stack = {};
-                    int stackSize = 0;
-                    ValueNode[] locks = {};
-                    List<MonitorIdNode> monitorIds = Collections.emptyList();
-                    stateAfterStart = graph.add(new FrameState(null, original, 0, locals, stack, stackSize, locks, monitorIds, false, false));
-                }
-                return stateAfterStart;
-            }
-
-            private void detectLoops(FixedNode startInstruction) {
-                NodeBitMap visited = graph.createNodeBitMap();
-                NodeBitMap active = graph.createNodeBitMap();
-                Deque<Node> stack = new ArrayDeque<>();
-                stack.add(startInstruction);
-                visited.mark(startInstruction);
-                while (!stack.isEmpty()) {
-                    Node next = stack.peek();
-                    assert next.isDeleted() || visited.isMarked(next);
-                    if (next.isDeleted() || active.isMarked(next)) {
-                        stack.pop();
-                        if (!next.isDeleted()) {
-                            active.clear(next);
-                        }
-                    } else {
-                        active.mark(next);
-                        for (Node n : next.cfgSuccessors()) {
-                            if (active.contains(n)) {
-                                // Detected cycle.
-                                assert n instanceof MergeNode;
-                                assert next instanceof EndNode;
-                                MergeNode merge = (MergeNode) n;
-                                EndNode endNode = (EndNode) next;
-                                merge.removeEnd(endNode);
-                                FixedNode afterMerge = merge.next();
-                                if (!(afterMerge instanceof EndNode) || !(((EndNode) afterMerge).merge() instanceof LoopBeginNode)) {
-                                    merge.setNext(null);
-                                    LoopBeginNode newLoopBegin = this.appendLoopBegin(merge);
-                                    newLoopBegin.setNext(afterMerge);
-                                }
-                                LoopBeginNode loopBegin = (LoopBeginNode) ((EndNode) merge.next()).merge();
-                                LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
-                                if (parsingIntrinsic()) {
-                                    loopEnd.disableSafepoint();
-                                }
-                                endNode.replaceAndDelete(loopEnd);
-                            } else if (visited.contains(n)) {
-                                // Normal merge into a branch we are already exploring.
-                            } else {
-                                visited.mark(n);
-                                stack.push(n);
-                            }
-                        }
-                    }
-                }
-
-                Debug.dump(graph, "After loops detected");
-                insertLoopEnds(startInstruction);
-            }
-
-            private void insertLoopEnds(FixedNode startInstruction) {
-                NodeBitMap visited = graph.createNodeBitMap();
-                Deque<Node> stack = new ArrayDeque<>();
-                stack.add(startInstruction);
-                visited.mark(startInstruction);
-                List<LoopBeginNode> loopBegins = new ArrayList<>();
-                while (!stack.isEmpty()) {
-                    Node next = stack.pop();
-                    assert visited.isMarked(next);
-                    if (next instanceof LoopBeginNode) {
-                        loopBegins.add((LoopBeginNode) next);
-                    }
-                    for (Node n : next.cfgSuccessors()) {
-                        if (visited.contains(n)) {
-                            // Nothing to do.
-                        } else {
-                            visited.mark(n);
-                            stack.push(n);
-                        }
-                    }
-                }
-
-                IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap = new IdentityHashMap<>();
-                for (int i = loopBegins.size() - 1; i >= 0; --i) {
-                    LoopBeginNode loopBegin = loopBegins.get(i);
-                    insertLoopExits(loopBegin, innerLoopsMap);
-                    if (DumpDuringGraphBuilding.getValue()) {
-                        Debug.dump(graph, "After building loop exits for %s.", loopBegin);
-                    }
-                }
-
-                // Remove degenerated merges with only one predecessor.
-                for (LoopBeginNode loopBegin : loopBegins) {
-                    Node pred = loopBegin.forwardEnd().predecessor();
-                    if (pred instanceof MergeNode) {
-                        MergeNode.removeMergeIfDegenerated((MergeNode) pred);
-                    }
-                }
-            }
-
-            private void insertLoopExits(LoopBeginNode loopBegin, IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap) {
-                NodeBitMap visited = graph.createNodeBitMap();
-                Deque<Node> stack = new ArrayDeque<>();
-                for (LoopEndNode loopEnd : loopBegin.loopEnds()) {
-                    stack.push(loopEnd);
-                    visited.mark(loopEnd);
-                }
-
-                List<ControlSplitNode> controlSplits = new ArrayList<>();
-                List<LoopBeginNode> innerLoopBegins = new ArrayList<>();
-
-                while (!stack.isEmpty()) {
-                    Node current = stack.pop();
-                    if (current == loopBegin) {
-                        continue;
-                    }
-                    for (Node pred : current.cfgPredecessors()) {
-                        if (!visited.isMarked(pred)) {
-                            visited.mark(pred);
-                            if (pred instanceof LoopExitNode) {
-                                // Inner loop
-                                LoopExitNode loopExitNode = (LoopExitNode) pred;
-                                LoopBeginNode innerLoopBegin = loopExitNode.loopBegin();
-                                if (!visited.isMarked(innerLoopBegin)) {
-                                    stack.push(innerLoopBegin);
-                                    visited.mark(innerLoopBegin);
-                                    innerLoopBegins.add(innerLoopBegin);
-                                }
-                            } else {
-                                if (pred instanceof ControlSplitNode) {
-                                    ControlSplitNode controlSplitNode = (ControlSplitNode) pred;
-                                    controlSplits.add(controlSplitNode);
-                                }
-                                stack.push(pred);
-                            }
-                        }
-                    }
-                }
-
-                for (ControlSplitNode controlSplit : controlSplits) {
-                    for (Node succ : controlSplit.cfgSuccessors()) {
-                        if (!visited.isMarked(succ)) {
-                            LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
-                            FixedNode next = ((FixedWithNextNode) succ).next();
-                            next.replaceAtPredecessor(loopExit);
-                            loopExit.setNext(next);
-                        }
-                    }
-                }
-
-                for (LoopBeginNode inner : innerLoopBegins) {
-                    addLoopExits(loopBegin, inner, innerLoopsMap, visited);
-                    if (DumpDuringGraphBuilding.getValue()) {
-                        Debug.dump(graph, "After adding loop exits for %s.", inner);
-                    }
-                }
-
-                innerLoopsMap.put(loopBegin, innerLoopBegins);
-            }
-
-            private void addLoopExits(LoopBeginNode loopBegin, LoopBeginNode inner, IdentityHashMap<LoopBeginNode, List<LoopBeginNode>> innerLoopsMap, NodeBitMap visited) {
-                for (LoopExitNode exit : inner.loopExits()) {
-                    if (!visited.isMarked(exit)) {
-                        LoopExitNode newLoopExit = graph.add(new LoopExitNode(loopBegin));
-                        FixedNode next = exit.next();
-                        next.replaceAtPredecessor(newLoopExit);
-                        newLoopExit.setNext(next);
-                    }
-                }
-
-                for (LoopBeginNode innerInner : innerLoopsMap.get(inner)) {
-                    addLoopExits(loopBegin, innerInner, innerLoopsMap, visited);
-                }
-            }
-
-            private int iterateBlock(BciBlock[] blocks, BciBlock block) {
-                if (block.isLoopHeader && this.explodeLoops) {
-                    return iterateExplodedLoopHeader(blocks, block);
-                } else {
-                    processBlock(this, block);
-                    return block.getId() + 1;
-                }
-            }
-
-            private int iterateExplodedLoopHeader(BciBlock[] blocks, BciBlock header) {
-                if (explodeLoopsContext == null) {
-                    explodeLoopsContext = new ArrayDeque<>();
-                }
-
-                ExplodedLoopContext context = new ExplodedLoopContext();
-                context.header = header;
-                context.peelIteration = this.getCurrentDimension();
-                if (this.mergeExplosions) {
-                    this.addToMergeCache(getEntryState(context.header, context.peelIteration), context.peelIteration);
-                }
-                explodeLoopsContext.push(context);
-                if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
-                    Debug.dump(graph, "before loop explosion dimension " + context.peelIteration);
-                }
-                peelIteration(blocks, header, context);
-                explodeLoopsContext.pop();
-                return header.loopEnd + 1;
-            }
-
-            private void addToMergeCache(FrameStateBuilder key, int dimension) {
-                mergeExplosionsMap.put(key, dimension);
-            }
-
-            private void peelIteration(BciBlock[] blocks, BciBlock header, ExplodedLoopContext context) {
-                while (true) {
-                    if (TraceParserPlugins.getValue()) {
-                        traceWithContext("exploding loop, iteration %d", context.peelIteration);
-                    }
-                    processBlock(this, header);
-                    int j = header.getId() + 1;
-                    while (j <= header.loopEnd) {
-                        BciBlock block = blocks[j];
-                        j = iterateBlock(blocks, block);
-                    }
-
-                    int[] targets = context.targetPeelIteration;
-                    if (targets != null) {
-                        // We were reaching the backedge during explosion. Explode further.
-                        for (int i = 0; i < targets.length; ++i) {
-                            context.peelIteration = targets[i];
-                            context.targetPeelIteration = null;
-                            if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
-                                Debug.dump(graph, "next loop explosion iteration " + context.peelIteration);
-                            }
-                            if (i < targets.length - 1) {
-                                peelIteration(blocks, header, context);
-                            }
-                        }
-                    } else {
-                        // We did not reach the backedge. Exit.
-                        break;
-                    }
-                }
-            }
-
-            /**
-             * @param type the unresolved type of the constant
-             */
-            protected void handleUnresolvedLoadConstant(JavaType type) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param type the unresolved type of the type check
-             * @param object the object value whose type is being checked against {@code type}
-             */
-            protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new FixedGuardNode(graph.unique(new IsNullNode(object)), Unresolved, InvalidateRecompile));
-                frameState.push(Kind.Object, appendConstant(JavaConstant.NULL_POINTER));
-            }
-
-            /**
-             * @param type the unresolved type of the type check
-             * @param object the object value whose type is being checked against {@code type}
-             */
-            protected void handleUnresolvedInstanceOf(JavaType type, ValueNode object) {
-                assert !graphBuilderConfig.eagerResolving();
-                AbstractBeginNode successor = graph.add(new BeginNode());
-                DeoptimizeNode deopt = graph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-                append(new IfNode(graph.unique(new IsNullNode(object)), successor, deopt, 1));
-                lastInstr = successor;
-                frameState.push(Kind.Int, appendConstant(JavaConstant.INT_0));
-            }
-
-            /**
-             * @param type the type being instantiated
-             */
-            protected void handleUnresolvedNewInstance(JavaType type) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param type the type of the array being instantiated
-             * @param length the length of the array
-             */
-            protected void handleUnresolvedNewObjectArray(JavaType type, ValueNode length) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param type the type being instantiated
-             * @param dims the dimensions for the multi-array
-             */
-            protected void handleUnresolvedNewMultiArray(JavaType type, List<ValueNode> dims) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param field the unresolved field
-             * @param receiver the object containing the field or {@code null} if {@code field} is
-             *            static
-             */
-            protected void handleUnresolvedLoadField(JavaField field, ValueNode receiver) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @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 void handleUnresolvedStoreField(JavaField field, ValueNode value, ValueNode receiver) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param type
-             */
-            protected void handleUnresolvedExceptionType(JavaType type) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            /**
-             * @param javaMethod
-             * @param invokeKind
-             */
-            protected void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKind) {
-                assert !graphBuilderConfig.eagerResolving();
-                append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-            }
-
-            private DispatchBeginNode handleException(ValueNode exceptionObject, int bci) {
-                assert bci == BytecodeFrame.BEFORE_BCI || bci == bci() : "invalid bci";
-                Debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci)));
-
-                BciBlock dispatchBlock = currentBlock.exceptionDispatchBlock();
-                /*
-                 * The exception dispatch block is always for the last bytecode of a block, so if we
-                 * are not at the endBci yet, there is no exception handler for this bci and we can
-                 * unwind immediately.
-                 */
-                if (bci != currentBlock.endBci || dispatchBlock == null) {
-                    dispatchBlock = blockMap.getUnwindBlock();
-                }
-
-                FrameStateBuilder dispatchState = frameState.copy();
-                dispatchState.clearStack();
-
-                DispatchBeginNode dispatchBegin;
-                if (exceptionObject == null) {
-                    dispatchBegin = graph.add(new ExceptionObjectNode(metaAccess));
-                    dispatchState.push(Kind.Object, dispatchBegin);
-                    dispatchState.setRethrowException(true);
-                    dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
-                } else {
-                    dispatchBegin = graph.add(new DispatchBeginNode());
-                    dispatchState.push(Kind.Object, exceptionObject);
-                    dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
-                    dispatchState.setRethrowException(true);
-                }
-                this.controlFlowSplit = true;
-                FixedNode target = createTarget(dispatchBlock, dispatchState);
-                FixedWithNextNode finishedDispatch = finishInstruction(dispatchBegin, dispatchState);
-                finishedDispatch.setNext(target);
-                return dispatchBegin;
-            }
-
-            protected ValueNode genLoadIndexed(ValueNode array, ValueNode index, Kind kind) {
-                return LoadIndexedNode.create(array, index, kind, metaAccess, constantReflection);
-            }
-
-            protected void genStoreIndexed(ValueNode array, ValueNode index, Kind kind, ValueNode value) {
-                add(new StoreIndexedNode(array, index, kind, value));
-            }
-
-            protected ValueNode genIntegerAdd(ValueNode x, ValueNode y) {
-                return AddNode.create(x, y);
-            }
-
-            protected ValueNode genIntegerSub(ValueNode x, ValueNode y) {
-                return SubNode.create(x, y);
-            }
-
-            protected ValueNode genIntegerMul(ValueNode x, ValueNode y) {
-                return MulNode.create(x, y);
-            }
-
-            protected ValueNode genFloatAdd(ValueNode x, ValueNode y) {
-                return AddNode.create(x, y);
-            }
-
-            protected ValueNode genFloatSub(ValueNode x, ValueNode y) {
-                return SubNode.create(x, y);
-            }
-
-            protected ValueNode genFloatMul(ValueNode x, ValueNode y) {
-                return MulNode.create(x, y);
-            }
-
-            protected ValueNode genFloatDiv(ValueNode x, ValueNode y) {
-                return DivNode.create(x, y);
-            }
-
-            protected ValueNode genFloatRem(ValueNode x, ValueNode y) {
-                return new RemNode(x, y);
-            }
-
-            protected ValueNode genIntegerDiv(ValueNode x, ValueNode y) {
-                return new IntegerDivNode(x, y);
-            }
-
-            protected ValueNode genIntegerRem(ValueNode x, ValueNode y) {
-                return new IntegerRemNode(x, y);
-            }
-
-            protected ValueNode genNegateOp(ValueNode x) {
-                return (new NegateNode(x));
-            }
-
-            protected ValueNode genLeftShift(ValueNode x, ValueNode y) {
-                return new LeftShiftNode(x, y);
-            }
-
-            protected ValueNode genRightShift(ValueNode x, ValueNode y) {
-                return new RightShiftNode(x, y);
-            }
-
-            protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) {
-                return new UnsignedRightShiftNode(x, y);
-            }
-
-            protected ValueNode genAnd(ValueNode x, ValueNode y) {
-                return AndNode.create(x, y);
-            }
-
-            protected ValueNode genOr(ValueNode x, ValueNode y) {
-                return OrNode.create(x, y);
-            }
-
-            protected ValueNode genXor(ValueNode x, ValueNode y) {
-                return XorNode.create(x, y);
-            }
-
-            protected ValueNode genNormalizeCompare(ValueNode x, ValueNode y, boolean isUnorderedLess) {
-                return NormalizeCompareNode.create(x, y, isUnorderedLess, constantReflection);
-            }
-
-            protected ValueNode genFloatConvert(FloatConvert op, ValueNode input) {
-                return FloatConvertNode.create(op, input);
-            }
-
-            protected ValueNode genNarrow(ValueNode input, int bitCount) {
-                return NarrowNode.create(input, bitCount);
-            }
-
-            protected ValueNode genSignExtend(ValueNode input, int bitCount) {
-                return SignExtendNode.create(input, bitCount);
-            }
-
-            protected ValueNode genZeroExtend(ValueNode input, int bitCount) {
-                return ZeroExtendNode.create(input, bitCount);
-            }
-
-            protected void genGoto() {
-                appendGoto(currentBlock.getSuccessor(0));
-                assert currentBlock.numNormalSuccessors() == 1;
-            }
-
-            protected LogicNode genObjectEquals(ValueNode x, ValueNode y) {
-                return ObjectEqualsNode.create(x, y, constantReflection);
-            }
-
-            protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) {
-                return IntegerEqualsNode.create(x, y, constantReflection);
-            }
-
-            protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) {
-                return IntegerLessThanNode.create(x, y, constantReflection);
-            }
-
-            protected ValueNode genUnique(ValueNode x) {
-                return (ValueNode) graph.unique((Node & ValueNumberable) x);
-            }
-
-            protected ValueNode genIfNode(LogicNode condition, FixedNode falseSuccessor, FixedNode trueSuccessor, double d) {
-                return new IfNode(condition, falseSuccessor, trueSuccessor, d);
-            }
-
-            protected void genThrow() {
-                ValueNode exception = frameState.pop(Kind.Object);
-                append(new FixedGuardNode(graph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
-                lastInstr.setNext(handleException(exception, bci()));
-            }
-
-            protected ValueNode createCheckCast(ResolvedJavaType type, ValueNode object, JavaTypeProfile profileForTypeCheck, boolean forStoreCheck) {
-                return CheckCastNode.create(type, object, profileForTypeCheck, forStoreCheck, graph.getAssumptions());
-            }
-
-            protected ValueNode createInstanceOf(ResolvedJavaType type, ValueNode object, JavaTypeProfile profileForTypeCheck) {
-                return InstanceOfNode.create(type, object, profileForTypeCheck);
-            }
-
-            protected ValueNode genConditional(ValueNode x) {
-                return new ConditionalNode((LogicNode) x);
-            }
-
-            protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) {
-                return new NewInstanceNode(type, fillContents);
-            }
-
-            protected NewArrayNode createNewArray(ResolvedJavaType elementType, ValueNode length, boolean fillContents) {
-                return new NewArrayNode(elementType, length, fillContents);
-            }
-
-            protected NewMultiArrayNode createNewMultiArray(ResolvedJavaType type, List<ValueNode> dimensions) {
-                return new NewMultiArrayNode(type, dimensions.toArray(new ValueNode[0]));
-            }
-
-            protected ValueNode genLoadField(ValueNode receiver, ResolvedJavaField field) {
-                return new LoadFieldNode(receiver, field);
-            }
-
-            protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
-                if (StampTool.isPointerNonNull(receiver.stamp())) {
-                    return receiver;
-                }
-                BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class));
-                AbstractBeginNode falseSucc = graph.add(new BeginNode());
-                PiNode nonNullReceiver = graph.unique(new PiNode(receiver, receiver.stamp().join(objectNonNull())));
-                nonNullReceiver.setGuard(falseSucc);
-                append(new IfNode(graph.unique(new IsNullNode(receiver)), exception, falseSucc, 0.01));
-                lastInstr = falseSucc;
-
-                exception.setStateAfter(createFrameState(bci(), exception));
-                exception.setNext(handleException(exception, bci()));
-                return nonNullReceiver;
-            }
-
-            protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) {
-                AbstractBeginNode trueSucc = graph.add(new BeginNode());
-                BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
-                append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
-                lastInstr = trueSucc;
-
-                exception.setStateAfter(createFrameState(bci(), exception));
-                exception.setNext(handleException(exception, bci()));
-            }
-
-            protected ValueNode genArrayLength(ValueNode x) {
-                return ArrayLengthNode.create(x, constantReflection);
-            }
-
-            protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) {
-                StoreFieldNode storeFieldNode = new StoreFieldNode(receiver, field, value);
-                append(storeFieldNode);
-                storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI(), storeFieldNode));
-            }
-
-            /**
-             * Ensure that concrete classes are at least linked before generating an invoke.
-             * Interfaces may never be linked so simply return true for them.
-             *
-             * @param target
-             * @return true if the declared holder is an interface or is linked
-             */
-            private boolean callTargetIsResolved(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
-                    ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
-                    ResolvedJavaType resolvedType = resolvedTarget.getDeclaringClass();
-                    return resolvedType.isInterface() || resolvedType.isLinked();
-                }
-                return false;
-            }
-
-            protected void genInvokeStatic(JavaMethod target) {
-                if (callTargetIsResolved(target)) {
-                    ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
-                    ResolvedJavaType holder = resolvedTarget.getDeclaringClass();
-                    if (!holder.isInitialized() && ResolveClassBeforeStaticInvoke.getValue()) {
-                        handleUnresolvedInvoke(target, InvokeKind.Static);
-                    } else {
-                        ValueNode[] args = frameState.popArguments(resolvedTarget.getSignature().getParameterCount(false));
-                        appendInvoke(InvokeKind.Static, resolvedTarget, args);
-                    }
-                } else {
-                    handleUnresolvedInvoke(target, InvokeKind.Static);
-                }
-            }
-
-            protected void genInvokeInterface(JavaMethod target) {
-                if (callTargetIsResolved(target)) {
-                    ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
-                    appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
-                } else {
-                    handleUnresolvedInvoke(target, InvokeKind.Interface);
-                }
-            }
-
-            protected void genInvokeDynamic(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
-                    JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI4(), Bytecodes.INVOKEDYNAMIC);
-                    if (appendix != null) {
-                        frameState.push(Kind.Object, ConstantNode.forConstant(appendix, metaAccess, graph));
-                    }
-                    ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(false));
-                    appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
-                } else {
-                    handleUnresolvedInvoke(target, InvokeKind.Static);
-                }
-            }
-
-            protected void genInvokeVirtual(JavaMethod target) {
-                if (callTargetIsResolved(target)) {
-                    /*
-                     * 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 = !((ResolvedJavaMethod) target).isStatic();
-                    JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI(), Bytecodes.INVOKEVIRTUAL);
-                    if (appendix != null) {
-                        frameState.push(Kind.Object, ConstantNode.forConstant(appendix, metaAccess, graph));
-                    }
-                    ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(hasReceiver));
-                    if (hasReceiver) {
-                        appendInvoke(InvokeKind.Virtual, (ResolvedJavaMethod) target, args);
-                    } else {
-                        appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args);
-                    }
-                } else {
-                    handleUnresolvedInvoke(target, InvokeKind.Virtual);
-                }
-
-            }
-
-            protected void genInvokeSpecial(JavaMethod target) {
-                if (callTargetIsResolved(target)) {
-                    assert target != null;
-                    assert target.getSignature() != null;
-                    ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
-                    appendInvoke(InvokeKind.Special, (ResolvedJavaMethod) target, args);
-                } else {
-                    handleUnresolvedInvoke(target, InvokeKind.Special);
-                }
-            }
-
-            private InvokeKind currentInvokeKind;
-            private JavaType currentInvokeReturnType;
-            protected FrameStateBuilder frameState;
-            protected BciBlock currentBlock;
-            protected final BytecodeStream stream;
-            protected final GraphBuilderConfiguration graphBuilderConfig;
-            protected final ResolvedJavaMethod method;
-            protected final ProfilingInfo profilingInfo;
-            protected final OptimisticOptimizations optimisticOpts;
-            protected final ConstantPool constantPool;
-            protected final MetaAccessProvider metaAccess;
-            protected final IntrinsicContext intrinsicContext;
-
-            public InvokeKind getInvokeKind() {
-                return currentInvokeKind;
-            }
-
-            public JavaType getInvokeReturnType() {
-                return currentInvokeReturnType;
-            }
-
-            public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) {
-                appendInvoke(invokeKind, targetMethod, args);
-            }
-
-            private void appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
-                ResolvedJavaMethod targetMethod = initialTargetMethod;
-                InvokeKind invokeKind = initialInvokeKind;
-                if (initialInvokeKind.isIndirect()) {
-                    ResolvedJavaType contextType = this.frameState.getMethod().getDeclaringClass();
-                    ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(initialInvokeKind, args[0], initialTargetMethod, contextType);
-                    if (specialCallTarget != null) {
-                        invokeKind = InvokeKind.Special;
-                        targetMethod = specialCallTarget;
-                    }
-                }
-
-                Kind resultType = targetMethod.getSignature().getReturnKind();
-                if (DeoptALot.getValue()) {
-                    append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
-                    frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, graph));
-                    return;
-                }
-
-                JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass());
-                if (graphBuilderConfig.eagerResolving() || parsingIntrinsic()) {
-                    returnType = returnType.resolve(targetMethod.getDeclaringClass());
-                }
-                if (invokeKind.hasReceiver()) {
-                    args[0] = emitExplicitExceptions(args[0], null);
-                    if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints()) {
-                        JavaTypeProfile profile = profilingInfo.getTypeProfile(bci());
-                        args[0] = TypeProfileProxyNode.proxify(args[0], profile);
-                    }
-
-                    if (args[0].isNullConstant()) {
-                        append(new DeoptimizeNode(InvalidateRecompile, NullCheckException));
-                        return;
-                    }
-                }
-
-                try {
-                    currentInvokeReturnType = returnType;
-                    currentInvokeKind = invokeKind;
-                    if (tryGenericInvocationPlugin(args, targetMethod)) {
-                        if (TraceParserPlugins.getValue()) {
-                            traceWithContext("used generic invocation plugin for %s", targetMethod.format("%h.%n(%p)"));
-                        }
-                        return;
-                    }
-
-                    if (invokeKind.isDirect()) {
-                        if (tryInvocationPlugin(args, targetMethod, resultType)) {
-                            if (TraceParserPlugins.getValue()) {
-                                traceWithContext("used invocation plugin for %s", targetMethod.format("%h.%n(%p)"));
-                            }
-                            return;
-                        }
-
-                        if (tryInline(args, targetMethod, returnType)) {
-                            return;
-                        }
-                    }
-                } finally {
-                    currentInvokeReturnType = null;
-                    currentInvokeKind = null;
-                }
-
-                MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, targetMethod, args, returnType));
-
-                // be conservative if information was not recorded (could result in endless
-                // recompiles otherwise)
-                Invoke invoke;
-                if (graphBuilderConfig.omitAllExceptionEdges() ||
-                                (!StressInvokeWithExceptionNode.getValue() && optimisticOpts.useExceptionProbability() && profilingInfo != null && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE)) {
-                    invoke = createInvoke(callTarget, resultType);
-                } else {
-                    invoke = createInvokeWithException(callTarget, resultType);
-                    AbstractBeginNode beginNode = graph.add(new KillingBeginNode(LocationIdentity.any()));
-                    invoke.setNext(beginNode);
-                    lastInstr = beginNode;
-                }
-
-                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
-                    plugin.notifyNotInlined(this, targetMethod, invoke);
-                }
-            }
-
-            /**
-             * Contains all the assertion checking logic around the application of an
-             * {@link InvocationPlugin}. This class is only loaded when assertions are enabled.
-             */
-            class InvocationPluginAssertions {
-                final InvocationPlugin plugin;
-                final ValueNode[] args;
-                final ResolvedJavaMethod targetMethod;
-                final Kind resultType;
-                final int beforeStackSize;
-                final boolean needsNullCheck;
-                final int nodeCount;
-                final Mark mark;
-
-                public InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, Kind resultType) {
-                    guarantee(assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
-                    this.plugin = plugin;
-                    this.targetMethod = targetMethod;
-                    this.args = args;
-                    this.resultType = resultType;
-                    this.beforeStackSize = frameState.stackSize();
-                    this.needsNullCheck = !targetMethod.isStatic() && args[0].getKind() == Kind.Object && !StampTool.isPointerNonNull(args[0].stamp());
-                    this.nodeCount = graph.getNodeCount();
-                    this.mark = graph.getMark();
-                }
-
-                String error(String format, Object... a) {
-                    return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getApplySourceLocation(metaAccess));
-                }
-
-                boolean check(boolean pluginResult) {
-                    if (pluginResult == true) {
-                        int expectedStackSize = beforeStackSize + resultType.getSlotCount();
-                        assert expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d", expectedStackSize, frameState.stackSize());
-                        NodeIterable<Node> newNodes = graph.getNewNodes(mark);
-                        assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]);
-                        for (Node n : newNodes) {
-                            if (n instanceof StateSplit) {
-                                StateSplit stateSplit = (StateSplit) n;
-                                assert stateSplit.stateAfter() != null || !stateSplit.hasSideEffect() : error("%s node added by plugin for %s need to have a non-null frame state: %s",
-                                                StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit);
-                            }
-                        }
-                        try {
-                            graphBuilderConfig.getPlugins().getInvocationPlugins().checkNewNodes(BytecodeParser.this, plugin, newNodes);
-                        } catch (Throwable t) {
-                            throw new AssertionError(error("Error in plugin"), t);
-                        }
-                    } else {
-                        assert nodeCount == graph.getNodeCount() : error("plugin that returns false must not create new nodes");
-                        assert beforeStackSize == frameState.stackSize() : error("plugin that returns false must not modify the stack");
-                    }
-                    return true;
-                }
-            }
-
-            private boolean tryInvocationPlugin(ValueNode[] args, ResolvedJavaMethod targetMethod, Kind resultType) {
-                InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
-                if (plugin != null) {
-
-                    if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) {
-                        // Self recursive intrinsic means the original
-                        // method should be called.
-                        assert !targetMethod.hasBytecodes() : "TODO: when does this happen?";
-                        return false;
-                    }
-
-                    InvocationPluginAssertions assertions = assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
-                    if (plugin.execute(this, targetMethod, invocationPluginReceiver.init(targetMethod, args), args)) {
-                        assert assertions.check(true);
-                        return true;
-                    }
-                    assert assertions.check(false);
-                }
-                return false;
-            }
-
-            private boolean tryGenericInvocationPlugin(ValueNode[] args, ResolvedJavaMethod targetMethod) {
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleInvoke(this, targetMethod, args)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, JavaType returnType) {
-                boolean canBeInlined = parsingIntrinsic() || targetMethod.canBeInlined();
-                if (!canBeInlined) {
-                    return false;
-                }
-                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
-                    InlineInfo inlineInfo = plugin.shouldInlineInvoke(this, targetMethod, args, returnType);
-                    if (inlineInfo != null) {
-                        if (inlineInfo.getMethodToInline() == null) {
-                            /* Do not inline, and do not ask the remaining plugins. */
-                            return false;
-                        } else {
-                            return inline(targetMethod, inlineInfo.getMethodToInline(), inlineInfo.isIntrinsic(), args);
-                        }
-                    }
-                }
-                return false;
-            }
-
-            public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) {
-                boolean res = inline(targetMethod, substitute, true, args);
-                assert res : "failed to inline " + substitute;
-            }
-
-            private boolean inline(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) {
-                if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) {
-                    if (targetMethod.equals(inlinedMethod)) {
-                        traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)"));
-                    } else {
-                        traceWithContext("inlining call to %s as intrinsic for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)"));
-                    }
-                }
-                IntrinsicContext intrinsic = this.intrinsicContext;
-                if (intrinsic != null && intrinsic.isCallToOriginal(targetMethod)) {
-                    if (intrinsic.isCompilationRoot()) {
-                        // A root compiled intrinsic needs to deoptimize
-                        // if the slow path is taken. During frame state
-                        // assignment, the deopt node will get its stateBefore
-                        // from the start node of the intrinsic
-                        append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint));
-                        return true;
-                    } else {
-                        // Otherwise inline the original method. Any frame state created
-                        // during the inlining will exclude frame(s) in the
-                        // intrinsic method (see HIRFrameStateBuilder.create(int bci)).
-                        if (intrinsic.getOriginalMethod().isNative()) {
-                            return false;
-                        }
-                        parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null);
-                        return true;
-                    }
-                } else {
-                    if (intrinsic == null && isIntrinsic) {
-                        assert !inlinedMethod.equals(targetMethod);
-                        intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, INLINE_DURING_PARSING);
-                    }
-                    if (inlinedMethod.hasBytecodes()) {
-                        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
-                            plugin.notifyBeforeInline(targetMethod);
-                        }
-                        parseAndInlineCallee(inlinedMethod, args, intrinsic);
-                        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
-                            plugin.notifyAfterInline(targetMethod);
-                        }
-                    } else {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            /**
-             * Prints a line to {@link TTY} with a prefix indicating the current parse context. The
-             * prefix is of the form:
-             *
-             * <pre>
-             * {SPACE * n} {name of method being parsed} "(" {file name} ":" {line number} ")"
-             * </pre>
-             *
-             * where {@code n} is the current inlining depth.
-             *
-             * @param format a format string
-             * @param args arguments to the format string
-             */
-
-            protected void traceWithContext(String format, Object... args) {
-                StackTraceElement where = method.asStackTraceElement(bci());
-                TTY.println(format("%s%s (%s:%d) %s", nSpaces(getDepth()), method.isConstructor() ? method.format("%h.%n") : method.getName(), where.getFileName(), where.getLineNumber(),
-                                format(format, args)));
-            }
-
-            protected BytecodeParserError asParserError(Throwable e) {
-                if (e instanceof BytecodeParserError) {
-                    return (BytecodeParserError) e;
-                }
-                BytecodeParser bp = this;
-                BytecodeParserError res = new BytecodeParserError(e);
-                while (bp != null) {
-                    res.addContext("parsing " + bp.method.asStackTraceElement(bp.bci()));
-                    bp = bp.parent;
-                }
-                return res;
-            }
-
-            private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) {
-                try (IntrinsicScope s = calleeIntrinsicContext != null && !parsingIntrinsic() ? new IntrinsicScope(this, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args)
-                                : null) {
-
-                    BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeIntrinsicContext);
-                    FrameStateBuilder startFrameState = new FrameStateBuilder(parser, targetMethod, graph);
-                    if (!targetMethod.isStatic()) {
-                        args[0] = nullCheckedValue(args[0]);
-                    }
-                    startFrameState.initializeFromArgumentsArray(args);
-                    parser.build(this.lastInstr, startFrameState);
-
-                    FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
-                    this.lastInstr = calleeBeforeReturnNode;
-                    Kind calleeReturnKind = targetMethod.getSignature().getReturnKind();
-                    if (calleeBeforeReturnNode != null) {
-                        ValueNode calleeReturnValue = parser.getReturnValue();
-                        if (calleeReturnValue != null) {
-                            frameState.push(calleeReturnKind.getStackKind(), calleeReturnValue);
-                        }
-                    }
-
-                    FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
-                    if (calleeBeforeUnwindNode != null) {
-                        ValueNode calleeUnwindValue = parser.getUnwindValue();
-                        assert calleeUnwindValue != null;
-                        calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
-                    }
-
-                    // Record inlined method dependency in the graph
-                    graph.recordInlinedMethod(targetMethod);
-                }
-            }
-
-            protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] 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);
-                invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
-                return invoke;
-            }
-
-            protected InvokeWithExceptionNode createInvokeWithException(CallTargetNode callTarget, Kind resultType) {
-                if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) {
-                    /*
-                     * Clear non-live locals early so that the exception handler entry gets the
-                     * cleared state.
-                     */
-                    frameState.clearNonLiveLocals(currentBlock, liveness, false);
-                }
-
-                DispatchBeginNode exceptionEdge = handleException(null, bci());
-                InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
-                frameState.pushReturn(resultType, invoke);
-                invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
-                return invoke;
-            }
-
-            protected void genReturn(ValueNode returnVal, Kind returnKind) {
-                if (parsingIntrinsic() && returnVal != null) {
-                    if (returnVal instanceof StateSplit) {
-                        StateSplit stateSplit = (StateSplit) returnVal;
-                        FrameState stateAfter = stateSplit.stateAfter();
-                        if (stateSplit.hasSideEffect()) {
-                            assert stateSplit != null;
-                            if (stateAfter.bci == BytecodeFrame.AFTER_BCI) {
-                                assert stateAfter.usages().count() == 1;
-                                assert stateAfter.usages().first() == stateSplit;
-                                stateAfter.replaceAtUsages(graph.add(new FrameState(BytecodeFrame.AFTER_BCI, returnVal)));
-                                GraphUtil.killWithUnusedFloatingInputs(stateAfter);
-                            } else {
-                                /*
-                                 * This must be the return value from within a partial
-                                 * intrinsification.
-                                 */
-                                assert !BytecodeFrame.isPlaceholderBci(stateAfter.bci);
-                            }
-                        } else {
-                            assert stateAfter == null;
-                        }
-                    }
-                }
-                if (parent == null) {
-                    frameState.setRethrowException(false);
-                    frameState.clearStack();
-                    beforeReturn(returnVal, returnKind);
-                    append(new ReturnNode(returnVal));
-                } else {
-                    if (blockMap.getReturnCount() == 1 || !controlFlowSplit) {
-                        // There is only a single return.
-                        beforeReturn(returnVal, returnKind);
-                        this.returnValue = returnVal;
-                        this.beforeReturnNode = this.lastInstr;
-                        this.lastInstr = null;
-                    } else {
-                        frameState.setRethrowException(false);
-                        frameState.clearStack();
-                        if (returnVal != null) {
-                            frameState.push(returnKind, returnVal);
-                        }
-                        assert blockMap.getReturnCount() > 1;
-                        appendGoto(blockMap.getReturnBlock());
-                    }
-                }
-            }
-
-            private void beforeReturn(ValueNode x, Kind kind) {
-                if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
-                    append(new RegisterFinalizerNode(frameState.loadLocal(0, Kind.Object)));
-                }
-                if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
-                    append(createInfoPointNode(InfopointReason.METHOD_END));
-                }
-
-                synchronizedEpilogue(BytecodeFrame.AFTER_BCI, x, kind);
-                if (frameState.lockDepth() != 0) {
-                    throw bailout("unbalanced monitors");
-                }
-            }
-
-            protected void genMonitorEnter(ValueNode x, int bci) {
-                MonitorIdNode monitorId = graph.add(new MonitorIdNode(frameState.lockDepth()));
-                MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, monitorId));
-                frameState.pushLock(x, monitorId);
-                monitorEnter.setStateAfter(createFrameState(bci, monitorEnter));
-            }
-
-            protected void genMonitorExit(ValueNode x, ValueNode escapedReturnValue, int bci) {
-                MonitorIdNode monitorId = frameState.peekMonitorId();
-                ValueNode lockedObject = frameState.popLock();
-                if (GraphUtil.originalValue(lockedObject) != GraphUtil.originalValue(x)) {
-                    throw bailout(String.format("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject)));
-                }
-                MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, escapedReturnValue));
-                monitorExit.setStateAfter(createFrameState(bci, monitorExit));
-            }
-
-            protected void genJsr(int dest) {
-                BciBlock successor = currentBlock.getJsrSuccessor();
-                assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci();
-                JsrScope scope = currentBlock.getJsrScope();
-                int nextBci = getStream().nextBCI();
-                if (!successor.getJsrScope().pop().equals(scope)) {
-                    throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
-                }
-                if (successor.getJsrScope().nextReturnAddress() != nextBci) {
-                    throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)");
-                }
-                ConstantNode nextBciNode = getJsrConstant(nextBci);
-                frameState.push(Kind.Object, nextBciNode);
-                appendGoto(successor);
-            }
-
-            protected void genRet(int localIndex) {
-                BciBlock successor = currentBlock.getRetSuccessor();
-                ValueNode local = frameState.loadLocal(localIndex, Kind.Object);
-                JsrScope scope = currentBlock.getJsrScope();
-                int retAddress = scope.nextReturnAddress();
-                ConstantNode returnBciNode = getJsrConstant(retAddress);
-                LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflection);
-                guard = graph.unique(guard);
-                append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
-                if (!successor.getJsrScope().equals(scope.pop())) {
-                    throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
-                }
-                appendGoto(successor);
-            }
-
-            private ConstantNode getJsrConstant(long bci) {
-                JavaConstant nextBciConstant = new RawConstant(bci);
-                Stamp nextBciStamp = StampFactory.forConstant(nextBciConstant);
-                ConstantNode nextBciNode = new ConstantNode(nextBciConstant, nextBciStamp);
-                return graph.unique(nextBciNode);
-            }
-
-            protected void genIntegerSwitch(ValueNode value, ArrayList<BciBlock> actualSuccessors, int[] keys, double[] keyProbabilities, int[] keySuccessors) {
-                if (value.isConstant()) {
-                    JavaConstant constant = (JavaConstant) value.asConstant();
-                    int constantValue = constant.asInt();
-                    for (int i = 0; i < keys.length; ++i) {
-                        if (keys[i] == constantValue) {
-                            appendGoto(actualSuccessors.get(keySuccessors[i]));
-                            return;
-                        }
-                    }
-                    appendGoto(actualSuccessors.get(keySuccessors[keys.length]));
-                } else {
-                    this.controlFlowSplit = true;
-                    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));
-                    }
-                }
-            }
-
-            protected ConstantNode appendConstant(JavaConstant constant) {
-                assert constant != null;
-                return ConstantNode.forConstant(constant, metaAccess, graph);
-            }
-
-            @Override
-            public <T extends ValueNode> T append(T v) {
-                if (v.graph() != null) {
-                    return v;
-                }
-                T added = graph.addOrUnique(v);
-                if (added == v) {
-                    updateLastInstruction(v);
-                }
-                return added;
-            }
-
-            public <T extends ValueNode> T recursiveAppend(T v) {
-                if (v.graph() != null) {
-                    return v;
-                }
-                T added = graph.addOrUniqueWithInputs(v);
-                if (added == v) {
-                    updateLastInstruction(v);
-                }
-                return added;
-            }
-
-            private <T extends ValueNode> void updateLastInstruction(T v) {
-                if (v instanceof FixedNode) {
-                    FixedNode fixedNode = (FixedNode) v;
-                    lastInstr.setNext(fixedNode);
-                    if (fixedNode instanceof FixedWithNextNode) {
-                        FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode;
-                        assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end";
-                        lastInstr = fixedWithNextNode;
-                    } else {
-                        lastInstr = null;
-                    }
-                }
-            }
-
-            private Target checkLoopExit(FixedNode target, BciBlock targetBlock, FrameStateBuilder state) {
-                if (currentBlock != null && !explodeLoops) {
-                    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(blockMap.getLoopHeader(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;
-                        }
-                        FrameStateBuilder newState = state.copy();
-                        for (BciBlock loop : exitLoops) {
-                            LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(loop, this.getCurrentDimension());
-                            LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
-                            if (lastLoopExit != null) {
-                                lastLoopExit.setNext(loopExit);
-                            }
-                            if (firstLoopExit == null) {
-                                firstLoopExit = loopExit;
-                            }
-                            lastLoopExit = loopExit;
-                            Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
-                            newState.insertLoopProxies(loopExit, getEntryState(loop, this.getCurrentDimension()));
-                            loopExit.setStateAfter(newState.create(bci, loopExit));
-                        }
-
-                        lastLoopExit.setNext(target);
-                        return new Target(firstLoopExit, newState);
-                    }
-                }
-                return new Target(target, state);
-            }
-
-            private FrameStateBuilder getEntryState(BciBlock block, int dimension) {
-                int id = block.id;
-                if (dimension == 0) {
-                    return entryStateArray[id];
-                } else {
-                    return getEntryStateMultiDimension(dimension, id);
-                }
-            }
-
-            private FrameStateBuilder getEntryStateMultiDimension(int dimension, int id) {
-                if (entryStateMatrix != null && dimension - 1 < entryStateMatrix.length) {
-                    FrameStateBuilder[] entryStateArrayEntry = entryStateMatrix[dimension - 1];
-                    if (entryStateArrayEntry == null) {
-                        return null;
-                    }
-                    return entryStateArrayEntry[id];
-                } else {
-                    return null;
-                }
-            }
-
-            private void setEntryState(BciBlock block, int dimension, FrameStateBuilder entryState) {
-                int id = block.id;
-                if (dimension == 0) {
-                    this.entryStateArray[id] = entryState;
-                } else {
-                    setEntryStateMultiDimension(dimension, entryState, id);
-                }
-            }
-
-            private void setEntryStateMultiDimension(int dimension, FrameStateBuilder entryState, int id) {
-                if (entryStateMatrix == null) {
-                    entryStateMatrix = new FrameStateBuilder[4][];
-                }
-                if (dimension - 1 < entryStateMatrix.length) {
-                    // We are within bounds.
-                } else {
-                    // We are out of bounds.
-                    entryStateMatrix = Arrays.copyOf(entryStateMatrix, Math.max(entryStateMatrix.length * 2, dimension));
-                }
-                if (entryStateMatrix[dimension - 1] == null) {
-                    entryStateMatrix[dimension - 1] = new FrameStateBuilder[blockMap.getBlockCount()];
-                }
-                entryStateMatrix[dimension - 1][id] = entryState;
-            }
-
-            private void setFirstInstruction(BciBlock block, int dimension, FixedWithNextNode firstInstruction) {
-                int id = block.id;
-                if (dimension == 0) {
-                    this.firstInstructionArray[id] = firstInstruction;
-                } else {
-                    setFirstInstructionMultiDimension(dimension, firstInstruction, id);
-                }
-            }
-
-            private void setFirstInstructionMultiDimension(int dimension, FixedWithNextNode firstInstruction, int id) {
-                if (firstInstructionMatrix == null) {
-                    firstInstructionMatrix = new FixedWithNextNode[4][];
-                }
-                if (dimension - 1 < firstInstructionMatrix.length) {
-                    // We are within bounds.
-                } else {
-                    // We are out of bounds.
-                    firstInstructionMatrix = Arrays.copyOf(firstInstructionMatrix, Math.max(firstInstructionMatrix.length * 2, dimension));
-                }
-                if (firstInstructionMatrix[dimension - 1] == null) {
-                    firstInstructionMatrix[dimension - 1] = new FixedWithNextNode[blockMap.getBlockCount()];
-                }
-                firstInstructionMatrix[dimension - 1][id] = firstInstruction;
-            }
-
-            private FixedWithNextNode getFirstInstruction(BciBlock block, int dimension) {
-                int id = block.id;
-                if (dimension == 0) {
-                    return firstInstructionArray[id];
-                } else {
-                    return getFirstInstructionMultiDimension(dimension, id);
-                }
-            }
-
-            private FixedWithNextNode getFirstInstructionMultiDimension(int dimension, int id) {
-                if (firstInstructionMatrix != null && dimension - 1 < firstInstructionMatrix.length) {
-                    FixedWithNextNode[] firstInstructionArrayEntry = firstInstructionMatrix[dimension - 1];
-                    if (firstInstructionArrayEntry == null) {
-                        return null;
-                    }
-                    return firstInstructionArrayEntry[id];
-                } else {
-                    return null;
-                }
-            }
-
-            private FixedNode createTarget(double probability, BciBlock block, FrameStateBuilder stateAfter) {
-                assert probability >= 0 && probability <= 1.01 : probability;
-                if (isNeverExecutedCode(probability)) {
-                    return graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
-                } else {
-                    assert block != null;
-                    return createTarget(block, stateAfter);
-                }
-            }
-
-            private FixedNode createTarget(BciBlock block, FrameStateBuilder state) {
-                return createTarget(block, state, false, false);
-            }
-
-            private FixedNode createTarget(BciBlock block, FrameStateBuilder state, boolean canReuseInstruction, boolean canReuseState) {
-                assert block != null && state != null;
-                assert !block.isExceptionEntry || state.stackSize() == 1;
-
-                int operatingDimension = findOperatingDimension(block, state);
-
-                if (getFirstInstruction(block, operatingDimension) == 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.
-                     */
-                    FixedNode targetNode;
-                    if (canReuseInstruction && (block.getPredecessorCount() == 1 || !controlFlowSplit) && !block.isLoopHeader && (currentBlock.loops & ~block.loops) == 0) {
-                        setFirstInstruction(block, operatingDimension, lastInstr);
-                        lastInstr = null;
-                    } else {
-                        setFirstInstruction(block, operatingDimension, graph.add(new BeginNode()));
-                    }
-                    targetNode = getFirstInstruction(block, operatingDimension);
-                    Target target = checkLoopExit(targetNode, block, state);
-                    FixedNode result = target.fixed;
-                    FrameStateBuilder currentEntryState = target.state == state ? (canReuseState ? state : state.copy()) : target.state;
-                    setEntryState(block, operatingDimension, currentEntryState);
-                    currentEntryState.clearNonLiveLocals(block, liveness, true);
-
-                    Debug.log("createTarget %s: first visit, result: %s", block, targetNode);
-                    return result;
-                }
-
-                // We already saw this block before, so we have to merge states.
-                if (!getEntryState(block, operatingDimension).isCompatibleWith(state)) {
-                    throw bailout("stacks do not match; bytecodes would not verify");
-                }
-
-                if (getFirstInstruction(block, operatingDimension) instanceof LoopBeginNode) {
-                    assert this.explodeLoops || (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) getFirstInstruction(block, operatingDimension);
-                    LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
-                    if (parsingIntrinsic()) {
-                        loopEnd.disableSafepoint();
-                    }
-                    Target target = checkLoopExit(loopEnd, block, state);
-                    FixedNode result = target.fixed;
-                    getEntryState(block, operatingDimension).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() || this.mergeExplosions : "must not be backward branch";
-                assert getFirstInstruction(block, operatingDimension).next() == null || this.mergeExplosions : "bytecodes already parsed for block";
-
-                if (getFirstInstruction(block, operatingDimension) instanceof AbstractBeginNode && !(getFirstInstruction(block, operatingDimension) instanceof AbstractMergeNode)) {
-                    /*
-                     * This is the second time we see this block. Create the actual MergeNode and
-                     * the End Node for the already existing edge.
-                     */
-                    AbstractBeginNode beginNode = (AbstractBeginNode) getFirstInstruction(block, operatingDimension);
-
-                    // The EndNode for the already existing edge.
-                    EndNode end = graph.add(new EndNode());
-                    // The MergeNode that replaces the placeholder.
-                    AbstractMergeNode mergeNode = graph.add(new MergeNode());
-                    FixedNode next = beginNode.next();
-
-                    if (beginNode.predecessor() instanceof ControlSplitNode) {
-                        beginNode.setNext(end);
-                    } else {
-                        beginNode.replaceAtPredecessor(end);
-                        beginNode.safeDelete();
-                    }
-
-                    mergeNode.addForwardEnd(end);
-                    mergeNode.setNext(next);
-
-                    setFirstInstruction(block, operatingDimension, mergeNode);
-                }
-
-                AbstractMergeNode mergeNode = (AbstractMergeNode) getFirstInstruction(block, operatingDimension);
-
-                // The EndNode for the newly merged edge.
-                EndNode newEnd = graph.add(new EndNode());
-                Target target = checkLoopExit(newEnd, block, state);
-                FixedNode result = target.fixed;
-                getEntryState(block, operatingDimension).merge(mergeNode, target.state);
-                mergeNode.addForwardEnd(newEnd);
-
-                Debug.log("createTarget %s: merging state, result: %s", block, result);
-                return result;
-            }
-
-            private int findOperatingDimension(BciBlock block, FrameStateBuilder state) {
-                if (this.explodeLoops && this.explodeLoopsContext != null && !this.explodeLoopsContext.isEmpty()) {
-                    return findOperatingDimensionWithLoopExplosion(block, state);
-                }
-                return this.getCurrentDimension();
-            }
-
-            private int findOperatingDimensionWithLoopExplosion(BciBlock block, FrameStateBuilder state) {
-                for (ExplodedLoopContext context : explodeLoopsContext) {
-                    if (context.header == block) {
-
-                        if (this.mergeExplosions) {
-                            state.clearNonLiveLocals(block, liveness, true);
-                            Integer cachedDimension = mergeExplosionsMap.get(state);
-                            if (cachedDimension != null) {
-                                return cachedDimension;
-                            }
-                        }
-
-                        // We have a hit on our current explosion context loop begin.
-                        if (context.targetPeelIteration == null) {
-                            context.targetPeelIteration = new int[1];
-                        } else {
-                            context.targetPeelIteration = Arrays.copyOf(context.targetPeelIteration, context.targetPeelIteration.length + 1);
-                        }
-
-                        // This is the first hit => allocate a new dimension and at the same
-                        // time mark the context loop begin as hit during the current
-                        // iteration.
-                        if (this.mergeExplosions) {
-                            this.addToMergeCache(state, nextPeelIteration);
-                        }
-                        context.targetPeelIteration[context.targetPeelIteration.length - 1] = nextPeelIteration++;
-                        if (nextPeelIteration > MaximumLoopExplosionCount.getValue()) {
-                            String message = "too many loop explosion iterations - does the explosion not terminate for method " + method + "?";
-                            if (FailedLoopExplosionIsFatal.getValue()) {
-                                throw new RuntimeException(message);
-                            } else {
-                                throw bailout(message);
-                            }
-                        }
-
-                        // Operate on the target dimension.
-                        return context.targetPeelIteration[context.targetPeelIteration.length - 1];
-                    } else if (block.getId() > context.header.getId() && block.getId() <= context.header.loopEnd) {
-                        // We hit the range of this context.
-                        return context.peelIteration;
-                    }
-                }
-
-                // No dimension found.
-                return 0;
-            }
-
-            /**
-             * 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, FrameStateBuilder stateAfter) {
-                FixedNode target = createTarget(probability, block, stateAfter);
-                AbstractBeginNode begin = BeginNode.begin(target);
-
-                assert !(target instanceof DeoptimizeNode && begin instanceof BeginStateSplitNode && ((BeginStateSplitNode) 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 ValueNode synchronizedObject(FrameStateBuilder state, ResolvedJavaMethod target) {
-                if (target.isStatic()) {
-                    return appendConstant(target.getDeclaringClass().getJavaClass());
-                } else {
-                    return state.loadLocal(0, Kind.Object);
-                }
-            }
-
-            protected void processBlock(BytecodeParser parser, BciBlock block) {
-                // Ignore blocks that have no predecessors by the time their bytecodes are parsed
-                int currentDimension = this.getCurrentDimension();
-                FixedWithNextNode firstInstruction = getFirstInstruction(block, currentDimension);
-                if (firstInstruction == null) {
-                    Debug.log("Ignoring block %s", block);
-                    return;
-                }
-                try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, firstInstruction, block.isLoopHeader)) {
-
-                    lastInstr = firstInstruction;
-                    frameState = getEntryState(block, currentDimension);
-                    parser.setCurrentFrameState(frameState);
-                    currentBlock = block;
-
-                    if (firstInstruction instanceof AbstractMergeNode) {
-                        setMergeStateAfter(block, firstInstruction);
-                    }
-
-                    if (block == blockMap.getReturnBlock()) {
-                        handleReturnBlock();
-                    } else if (block == blockMap.getUnwindBlock()) {
-                        handleUnwindBlock();
-                    } else if (block instanceof ExceptionDispatchBlock) {
-                        createExceptionDispatch((ExceptionDispatchBlock) block);
-                    } else {
-                        frameState.setRethrowException(false);
-                        iterateBytecodesForBlock(block);
-                    }
-                }
-            }
-
-            private void handleUnwindBlock() {
-                if (parent == null) {
-                    frameState.setRethrowException(false);
-                    createUnwind();
-                } else {
-                    ValueNode exception = frameState.pop(Kind.Object);
-                    this.unwindValue = exception;
-                    this.beforeUnwindNode = this.lastInstr;
-                }
-            }
-
-            private void handleReturnBlock() {
-                Kind returnKind = method.getSignature().getReturnKind().getStackKind();
-                ValueNode x = returnKind == Kind.Void ? null : frameState.pop(returnKind);
-                assert frameState.stackSize() == 0;
-                beforeReturn(x, returnKind);
-                this.returnValue = x;
-                this.beforeReturnNode = this.lastInstr;
-            }
-
-            private void setMergeStateAfter(BciBlock block, FixedWithNextNode firstInstruction) {
-                AbstractMergeNode abstractMergeNode = (AbstractMergeNode) firstInstruction;
-                if (abstractMergeNode.stateAfter() == null) {
-                    int bci = block.startBci;
-                    if (block instanceof ExceptionDispatchBlock) {
-                        bci = ((ExceptionDispatchBlock) block).deoptBci;
-                    }
-                    abstractMergeNode.setStateAfter(createFrameState(bci, abstractMergeNode));
-                }
-            }
-
-            private void createUnwind() {
-                assert frameState.stackSize() == 1 : frameState;
-                ValueNode exception = frameState.pop(Kind.Object);
-                synchronizedEpilogue(BytecodeFrame.AFTER_EXCEPTION_BCI, null, null);
-                append(new UnwindNode(exception));
-            }
-
-            private void synchronizedEpilogue(int bci, ValueNode currentReturnValue, Kind currentReturnValueKind) {
-                if (method.isSynchronized()) {
-                    if (currentReturnValue != null) {
-                        frameState.push(currentReturnValueKind, currentReturnValue);
-                    }
-                    genMonitorExit(methodSynchronizedObject, currentReturnValue, bci);
-                    assert !frameState.rethrowException();
-                }
-            }
-
-            private void createExceptionDispatch(ExceptionDispatchBlock block) {
-                assert frameState.stackSize() == 1 : frameState;
-                if (block.handler.isCatchAll()) {
-                    assert block.getSuccessorCount() == 1;
-                    appendGoto(block.getSuccessor(0));
-                    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 ? blockMap.getUnwindBlock() : block.getSuccessor(1);
-                            ValueNode exception = frameState.stack[0];
-                            FixedNode trueSuccessor = graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
-                            FixedNode nextDispatch = createTarget(nextBlock, frameState);
-                            append(new IfNode(graph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), trueSuccessor, nextDispatch, 0));
-                            return;
-                        }
-                    }
-                }
-
-                if (initialized) {
-                    BciBlock nextBlock = block.getSuccessorCount() == 1 ? blockMap.getUnwindBlock() : block.getSuccessor(1);
-                    ValueNode exception = frameState.stack[0];
-                    CheckCastNode checkCast = graph.add(new CheckCastNode((ResolvedJavaType) catchType, exception, null, false));
-                    frameState.pop(Kind.Object);
-                    frameState.push(Kind.Object, checkCast);
-                    FixedNode catchSuccessor = createTarget(block.getSuccessor(0), frameState);
-                    frameState.pop(Kind.Object);
-                    frameState.push(Kind.Object, exception);
-                    FixedNode nextDispatch = createTarget(nextBlock, frameState);
-                    checkCast.setNext(catchSuccessor);
-                    append(new IfNode(graph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
-                } else {
-                    handleUnresolvedExceptionType(catchType);
-                }
-            }
-
-            private void appendGoto(BciBlock successor) {
-                FixedNode targetInstr = createTarget(successor, frameState, true, true);
-                if (lastInstr != null && lastInstr != targetInstr) {
-                    lastInstr.setNext(targetInstr);
-                }
-            }
-
-            protected void iterateBytecodesForBlock(BciBlock block) {
-                if (block.isLoopHeader && !explodeLoops) {
-                    // Create the loop header block, which later will merge the backward branches of
-                    // the loop.
-                    controlFlowSplit = true;
-                    LoopBeginNode loopBegin = appendLoopBegin(this.lastInstr);
-                    lastInstr = loopBegin;
-
-                    // Create phi functions for all local variables and operand stack slots.
-                    frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis());
-                    loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin));
-
-                    /*
-                     * 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.
-                     */
-                    setFirstInstruction(block, this.getCurrentDimension(), 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.
-                     */
-                    setEntryState(block, this.getCurrentDimension(), frameState.copy());
-
-                    Debug.log("  created loop header %s", loopBegin);
-                } else if (block.isLoopHeader && explodeLoops && this.mergeExplosions) {
-                    frameState = frameState.copy();
-                }
-                assert lastInstr.next() == null : "instructions already appended at block " + block;
-                Debug.log("  frameState: %s", frameState);
-
-                lastInstr = finishInstruction(lastInstr, frameState);
-
-                int endBCI = stream.endBCI();
-
-                stream.setBCI(block.startBci);
-                int bci = block.startBci;
-                BytecodesParsed.add(block.endBci - bci);
-
-                /* Reset line number for new block */
-                if (graphBuilderConfig.insertSimpleDebugInfo()) {
-                    previousLineNumber = -1;
-                }
-
-                while (bci < endBCI) {
-                    if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
-                        currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : (graphBuilderConfig.insertFullDebugInfo() ? -1 : bci);
-                        if (currentLineNumber != previousLineNumber) {
-                            append(createInfoPointNode(InfopointReason.LINE_NUMBER));
-                            previousLineNumber = currentLineNumber;
-                        }
-                    }
-
-                    // read the opcode
-                    int opcode = stream.currentBC();
-                    assert traceState();
-                    assert traceInstruction(bci, opcode, bci == block.startBci);
-                    if (parent == null && bci == entryBCI) {
-                        if (block.getJsrScope() != JsrScope.EMPTY_SCOPE) {
-                            throw new BailoutException("OSR into a JSR scope is not supported");
-                        }
-                        EntryMarkerNode x = append(new EntryMarkerNode());
-                        frameState.insertProxies(x);
-                        x.setStateAfter(createFrameState(bci, x));
-                    }
-
-                    try {
-                        processBytecode(bci, opcode);
-                    } catch (Throwable e) {
-                        throw asParserError(e);
-                    }
-
-                    if (lastInstr == null || lastInstr.next() != null) {
-                        break;
-                    }
-
-                    stream.next();
-                    bci = stream.currentBCI();
-
-                    assert block == currentBlock;
-                    assert checkLastInstruction();
-                    lastInstr = finishInstruction(lastInstr, frameState);
-                    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(block.getSuccessor(0));
-                            break;
-                        }
-                    }
-                }
-            }
-
-            /* Also a hook for subclasses. */
-            protected boolean forceLoopPhis() {
-                return graph.isOSR();
-            }
-
-            protected boolean checkLastInstruction() {
-                if (lastInstr instanceof BeginNode) {
-                    // ignore
-                } else if (lastInstr instanceof StateSplit) {
-                    StateSplit stateSplit = (StateSplit) lastInstr;
-                    if (stateSplit.hasSideEffect()) {
-                        assert stateSplit.stateAfter() != null : "side effect " + lastInstr + " requires a non-null stateAfter";
-                    }
-                }
-                return true;
-            }
-
-            private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext) {
-                EndNode preLoopEnd = graph.add(new EndNode());
-                LoopBeginNode loopBegin = graph.add(new LoopBeginNode());
-                fixedWithNext.setNext(preLoopEnd);
-                // Add the single non-loop predecessor of the loop header.
-                loopBegin.addForwardEnd(preLoopEnd);
-                return loopBegin;
-            }
-
-            /**
-             * A hook for derived classes to modify the last instruction or add other instructions.
-             *
-             * @param instr The last instruction (= fixed node) which was added.
-             * @param state The current frame state.
-             * @return Returns the (new) last instruction.
-             */
-            protected FixedWithNextNode finishInstruction(FixedWithNextNode instr, FrameStateBuilder state) {
-                return instr;
-            }
-
-            private InfopointNode createInfoPointNode(InfopointReason reason) {
-                if (graphBuilderConfig.insertFullDebugInfo()) {
-                    return new FullInfopointNode(reason, createFrameState(bci(), null));
-                } else {
-                    BytecodePosition position = createBytecodePosition();
-                    // Update the previous infopoint position if no new fixed nodes were inserted
-                    if (lastInstr instanceof SimpleInfopointNode) {
-                        SimpleInfopointNode lastInfopoint = (SimpleInfopointNode) lastInstr;
-                        if (lastInfopoint.getReason() == reason) {
-                            lastInfopoint.setPosition(position);
-                            return lastInfopoint;
-                        }
-                    }
-                    return new SimpleInfopointNode(reason, position);
-                }
-            }
-
-            private boolean traceState() {
-                if (Debug.isEnabled() && Options.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
-                    frameState.traceState();
-                }
-                return true;
-            }
-
-            protected void genIf(ValueNode x, Condition cond, ValueNode y) {
-                assert x.getKind().getStackKind() == y.getKind().getStackKind();
-                assert currentBlock.getSuccessorCount() == 2;
-                BciBlock trueBlock = currentBlock.getSuccessor(0);
-                BciBlock falseBlock = currentBlock.getSuccessor(1);
-                if (trueBlock == falseBlock) {
-                    // The target block is the same independent of the condition.
-                    appendGoto(trueBlock);
-                    return;
-                }
-
-                ValueNode a = x;
-                ValueNode b = y;
-
-                // Check whether the condition needs to mirror the operands.
-                if (cond.canonicalMirror()) {
-                    a = y;
-                    b = x;
-                }
-
-                // Create the logic node for the condition.
-                LogicNode condition = createLogicNode(cond, a, b);
-
-                // Check whether the condition needs to negate the result.
-                boolean negate = cond.canonicalNegate();
-
-                // Remove a logic negation node and fold it into the negate boolean.
-                if (condition instanceof LogicNegationNode) {
-                    LogicNegationNode logicNegationNode = (LogicNegationNode) condition;
-                    negate = !negate;
-                    condition = logicNegationNode.getValue();
-                }
-
-                if (condition instanceof LogicConstantNode) {
-                    genConstantTargetIf(trueBlock, falseBlock, negate, condition);
-                } else {
-                    if (condition.graph() == null) {
-                        condition = graph.unique(condition);
-                    }
-
-                    // Need to get probability based on current bci.
-                    double probability = branchProbability();
-
-                    if (negate) {
-                        BciBlock tmpBlock = trueBlock;
-                        trueBlock = falseBlock;
-                        falseBlock = tmpBlock;
-                        probability = 1 - probability;
-                    }
-
-                    if (isNeverExecutedCode(probability)) {
-                        append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, true));
-                        appendGoto(falseBlock);
-                        return;
-                    } else if (isNeverExecutedCode(1 - probability)) {
-                        append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, false));
-                        appendGoto(trueBlock);
-                        return;
-                    }
-
-                    int oldBci = stream.currentBCI();
-                    int trueBlockInt = checkPositiveIntConstantPushed(trueBlock);
-                    if (trueBlockInt != -1) {
-                        int falseBlockInt = checkPositiveIntConstantPushed(falseBlock);
-                        if (falseBlockInt != -1) {
-                            if (tryGenConditionalForIf(trueBlock, falseBlock, condition, oldBci, trueBlockInt, falseBlockInt)) {
-                                return;
-                            }
-                        }
-                    }
-
-                    this.controlFlowSplit = true;
-                    FixedNode trueSuccessor = createTarget(trueBlock, frameState, false, false);
-                    FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true);
-                    ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability);
-                    append(ifNode);
-                    if (parsingIntrinsic()) {
-                        if (x instanceof BranchProbabilityNode) {
-                            ((BranchProbabilityNode) x).simplify(null);
-                        } else if (y instanceof BranchProbabilityNode) {
-                            ((BranchProbabilityNode) y).simplify(null);
-                        }
-                    }
-                }
-            }
-
-            private boolean tryGenConditionalForIf(BciBlock trueBlock, BciBlock falseBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt) {
-                if (gotoOrFallThroughAfterConstant(trueBlock) && gotoOrFallThroughAfterConstant(falseBlock) && trueBlock.getSuccessor(0) == falseBlock.getSuccessor(0)) {
-                    genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, false);
-                    return true;
-                } else if (this.parent != null && returnAfterConstant(trueBlock) && returnAfterConstant(falseBlock)) {
-                    genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, true);
-                    return true;
-                }
-                return false;
-            }
-
-            private void genConditionalForIf(BciBlock trueBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt, boolean genReturn) {
-                ConstantNode trueValue = graph.unique(ConstantNode.forInt(trueBlockInt));
-                ConstantNode falseValue = graph.unique(ConstantNode.forInt(falseBlockInt));
-                ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue);
-                if (conditionalNode.graph() == null) {
-                    conditionalNode = graph.addOrUnique(conditionalNode);
-                }
-                if (genReturn) {
-                    Kind returnKind = method.getSignature().getReturnKind().getStackKind();
-                    this.genReturn(conditionalNode, returnKind);
-                } else {
-                    frameState.push(Kind.Int, conditionalNode);
-                    appendGoto(trueBlock.getSuccessor(0));
-                    stream.setBCI(oldBci);
-                }
-            }
-
-            private LogicNode createLogicNode(Condition cond, ValueNode a, ValueNode b) {
-                LogicNode condition;
-                assert !a.getKind().isNumericFloat();
-                if (cond == Condition.EQ || cond == Condition.NE) {
-                    if (a.getKind() == Kind.Object) {
-                        condition = genObjectEquals(a, b);
-                    } else {
-                        condition = genIntegerEquals(a, b);
-                    }
-                } else {
-                    assert a.getKind() != Kind.Object && !cond.isUnsigned();
-                    condition = genIntegerLessThan(a, b);
-                }
-                return condition;
-            }
-
-            private void genConstantTargetIf(BciBlock trueBlock, BciBlock falseBlock, boolean negate, LogicNode condition) {
-                LogicConstantNode constantLogicNode = (LogicConstantNode) condition;
-                boolean value = constantLogicNode.getValue();
-                if (negate) {
-                    value = !value;
-                }
-                BciBlock nextBlock = falseBlock;
-                if (value) {
-                    nextBlock = trueBlock;
-                }
-                appendGoto(nextBlock);
-            }
-
-            private int checkPositiveIntConstantPushed(BciBlock block) {
-                stream.setBCI(block.startBci);
-                int currentBC = stream.currentBC();
-                if (currentBC >= Bytecodes.ICONST_0 && currentBC <= Bytecodes.ICONST_5) {
-                    int constValue = currentBC - Bytecodes.ICONST_0;
-                    return constValue;
-                }
-                return -1;
-            }
-
-            private boolean gotoOrFallThroughAfterConstant(BciBlock block) {
-                stream.setBCI(block.startBci);
-                int currentBCI = stream.nextBCI();
-                stream.setBCI(currentBCI);
-                int currentBC = stream.currentBC();
-                return stream.currentBCI() > block.endBci || currentBC == Bytecodes.GOTO || currentBC == Bytecodes.GOTO_W;
-            }
-
-            private boolean returnAfterConstant(BciBlock block) {
-                stream.setBCI(block.startBci);
-                int currentBCI = stream.nextBCI();
-                stream.setBCI(currentBCI);
-                int currentBC = stream.currentBC();
-                return currentBC == Bytecodes.IRETURN;
-            }
-
-            public StampProvider getStampProvider() {
-                return stampProvider;
-            }
-
-            public MetaAccessProvider getMetaAccess() {
-                return metaAccess;
-            }
-
-            public void push(Kind slotKind, ValueNode value) {
-                assert value.isAlive();
-                frameState.push(slotKind, value);
-            }
-
-            private int getCurrentDimension() {
-                if (this.explodeLoopsContext == null || this.explodeLoopsContext.isEmpty()) {
-                    return 0;
-                } else {
-                    return this.explodeLoopsContext.peek().peelIteration;
-                }
-            }
-
-            public ConstantReflectionProvider getConstantReflection() {
-                return constantReflection;
-            }
-
-            /**
-             * Gets the graph being processed by this builder.
-             */
-            public StructuredGraph getGraph() {
-                return graph;
-            }
-
-            public BytecodeParser getParent() {
-                return parent;
-            }
-
-            public IntrinsicContext getIntrinsic() {
-                return intrinsicContext;
-            }
-
-            @Override
-            public String toString() {
-                Formatter fmt = new Formatter();
-                BytecodeParser bp = this;
-                String indent = "";
-                while (bp != null) {
-                    if (bp != this) {
-                        fmt.format("%n%s", indent);
-                    }
-                    fmt.format("%s [bci: %d, intrinsic: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingIntrinsic());
-                    fmt.format("%n%s", new BytecodeDisassembler().disassemble(bp.method, bp.bci(), bp.bci() + 10));
-                    bp = bp.parent;
-                    indent += " ";
-                }
-                return fmt.toString();
-            }
-
-            public BailoutException bailout(String string) {
-                FrameState currentFrameState = createFrameState(bci(), null);
-                StackTraceElement[] elements = GraphUtil.approxSourceStackTraceElement(currentFrameState);
-                BailoutException bailout = new BailoutException(string);
-                throw GraphUtil.createBailoutException(string, bailout, elements);
-            }
-
-            private FrameState createFrameState(int bci, StateSplit forStateSplit) {
-                if (currentBlock != null && bci > currentBlock.endBci) {
-                    frameState.clearNonLiveLocals(currentBlock, liveness, false);
-                }
-                return frameState.create(bci, forStateSplit);
-            }
-
-            public void setStateAfter(StateSplit sideEffect) {
-                assert sideEffect.hasSideEffect();
-                FrameState stateAfter = createFrameState(stream.nextBCI(), sideEffect);
-                sideEffect.setStateAfter(stateAfter);
-            }
-
-            private BytecodePosition createBytecodePosition() {
-                return frameState.createBytecodePosition(bci());
-            }
-
-            public void setCurrentFrameState(FrameStateBuilder frameState) {
-                this.frameState = frameState;
-            }
-
-            protected final BytecodeStream getStream() {
-                return stream;
-            }
-
-            public int bci() {
-                return stream.currentBCI();
-            }
-
-            public void loadLocal(int index, Kind kind) {
-                ValueNode value = frameState.loadLocal(index, kind);
-                frameState.push(kind, value);
-            }
-
-            public void storeLocal(Kind kind, int index) {
-                ValueNode value = frameState.pop(kind);
-                frameState.storeLocal(index, kind, value);
-            }
-
-            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).getJavaClass()));
-                    } else {
-                        handleUnresolvedLoadConstant(type);
-                    }
-                } else if (con instanceof JavaConstant) {
-                    JavaConstant constant = (JavaConstant) con;
-                    frameState.push(constant.getKind(), appendConstant(constant));
-                } else {
-                    throw new Error("lookupConstant returned an object of incorrect type");
-                }
-            }
-
-            private void genLoadIndexed(Kind kind) {
-                ValueNode index = frameState.pop(Kind.Int);
-                ValueNode array = emitExplicitExceptions(frameState.pop(Kind.Object), index);
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleLoadIndexed(this, array, index, kind)) {
-                        return;
-                    }
-                }
-
-                frameState.push(kind, append(genLoadIndexed(array, index, kind)));
-            }
-
-            private void genStoreIndexed(Kind kind) {
-                ValueNode value = frameState.pop(kind);
-                ValueNode index = frameState.pop(Kind.Int);
-                ValueNode array = emitExplicitExceptions(frameState.pop(Kind.Object), index);
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleStoreIndexed(this, array, index, kind, value)) {
-                        return;
-                    }
-                }
-
-                genStoreIndexed(array, index, kind, value);
-            }
-
-            private void genArithmeticOp(Kind kind, int opcode) {
-                ValueNode y = frameState.pop(kind);
-                ValueNode x = frameState.pop(kind);
-                ValueNode v;
-                switch (opcode) {
-                    case IADD:
-                    case LADD:
-                        v = genIntegerAdd(x, y);
-                        break;
-                    case FADD:
-                    case DADD:
-                        v = genFloatAdd(x, y);
-                        break;
-                    case ISUB:
-                    case LSUB:
-                        v = genIntegerSub(x, y);
-                        break;
-                    case FSUB:
-                    case DSUB:
-                        v = genFloatSub(x, y);
-                        break;
-                    case IMUL:
-                    case LMUL:
-                        v = genIntegerMul(x, y);
-                        break;
-                    case FMUL:
-                    case DMUL:
-                        v = genFloatMul(x, y);
-                        break;
-                    case FDIV:
-                    case DDIV:
-                        v = genFloatDiv(x, y);
-                        break;
-                    case FREM:
-                    case DREM:
-                        v = genFloatRem(x, y);
-                        break;
-                    default:
-                        throw shouldNotReachHere();
-                }
-                frameState.push(kind, append(v));
-            }
-
-            private void genIntegerDivOp(Kind kind, int opcode) {
-                ValueNode y = frameState.pop(kind);
-                ValueNode x = frameState.pop(kind);
-                ValueNode v;
-                switch (opcode) {
-                    case IDIV:
-                    case LDIV:
-                        v = genIntegerDiv(x, y);
-                        break;
-                    case IREM:
-                    case LREM:
-                        v = genIntegerRem(x, y);
-                        break;
-                    default:
-                        throw shouldNotReachHere();
-                }
-                frameState.push(kind, append(v));
-            }
-
-            private void genNegateOp(Kind kind) {
-                ValueNode x = frameState.pop(kind);
-                frameState.push(kind, append(genNegateOp(x)));
-            }
-
-            private void genShiftOp(Kind kind, int opcode) {
-                ValueNode s = frameState.pop(Kind.Int);
-                ValueNode x = frameState.pop(kind);
-                ValueNode v;
-                switch (opcode) {
-                    case ISHL:
-                    case LSHL:
-                        v = genLeftShift(x, s);
-                        break;
-                    case ISHR:
-                    case LSHR:
-                        v = genRightShift(x, s);
-                        break;
-                    case IUSHR:
-                    case LUSHR:
-                        v = genUnsignedRightShift(x, s);
-                        break;
-                    default:
-                        throw shouldNotReachHere();
-                }
-                frameState.push(kind, append(v));
-            }
-
-            private void genLogicOp(Kind kind, int opcode) {
-                ValueNode y = frameState.pop(kind);
-                ValueNode x = frameState.pop(kind);
-                ValueNode v;
-                switch (opcode) {
-                    case IAND:
-                    case LAND:
-                        v = genAnd(x, y);
-                        break;
-                    case IOR:
-                    case LOR:
-                        v = genOr(x, y);
-                        break;
-                    case IXOR:
-                    case LXOR:
-                        v = genXor(x, y);
-                        break;
-                    default:
-                        throw shouldNotReachHere();
-                }
-                frameState.push(kind, append(v));
-            }
-
-            private void genCompareOp(Kind kind, boolean isUnorderedLess) {
-                ValueNode y = frameState.pop(kind);
-                ValueNode x = frameState.pop(kind);
-                frameState.push(Kind.Int, append(genNormalizeCompare(x, y, isUnorderedLess)));
-            }
-
-            private void genFloatConvert(FloatConvert op, Kind from, Kind to) {
-                ValueNode input = frameState.pop(from);
-                frameState.push(to, append(genFloatConvert(op, input)));
-            }
-
-            private void genSignExtend(Kind from, Kind to) {
-                ValueNode input = frameState.pop(from);
-                if (from != from.getStackKind()) {
-                    input = append(genNarrow(input, from.getBitCount()));
-                }
-                frameState.push(to, append(genSignExtend(input, to.getBitCount())));
-            }
-
-            private void genZeroExtend(Kind from, Kind to) {
-                ValueNode input = frameState.pop(from);
-                if (from != from.getStackKind()) {
-                    input = append(genNarrow(input, from.getBitCount()));
-                }
-                frameState.push(to, append(genZeroExtend(input, to.getBitCount())));
-            }
-
-            private void genNarrow(Kind from, Kind to) {
-                ValueNode input = frameState.pop(from);
-                frameState.push(to, append(genNarrow(input, to.getBitCount())));
-            }
-
-            private void genIncrement() {
-                int index = getStream().readLocalIndex();
-                int delta = getStream().readIncrement();
-                ValueNode x = frameState.loadLocal(index, Kind.Int);
-                ValueNode y = appendConstant(JavaConstant.forInt(delta));
-                frameState.storeLocal(index, Kind.Int, append(genIntegerAdd(x, y)));
-            }
-
-            private void genIfZero(Condition cond) {
-                ValueNode y = appendConstant(JavaConstant.INT_0);
-                ValueNode x = frameState.pop(Kind.Int);
-                genIf(x, cond, y);
-            }
-
-            private void genIfNull(Condition cond) {
-                ValueNode y = appendConstant(JavaConstant.NULL_POINTER);
-                ValueNode x = frameState.pop(Kind.Object);
-                genIf(x, cond, y);
-            }
-
-            private void genIfSame(Kind kind, Condition cond) {
-                ValueNode y = frameState.pop(kind);
-                ValueNode x = frameState.pop(kind);
-                genIf(x, cond, y);
-            }
-
-            protected JavaType lookupType(int cpi, int bytecode) {
-                maybeEagerlyResolve(cpi, bytecode);
-                JavaType result = constantPool.lookupType(cpi, bytecode);
-                assert !graphBuilderConfig.unresolvedIsError() || result instanceof ResolvedJavaType;
-                return result;
-            }
-
-            private JavaMethod lookupMethod(int cpi, int opcode) {
-                maybeEagerlyResolve(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) {
-                maybeEagerlyResolve(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) {
-                maybeEagerlyResolve(cpi, opcode);
-                Object result = constantPool.lookupConstant(cpi);
-                assert !graphBuilderConfig.eagerResolving() || !(result instanceof JavaType) || (result instanceof ResolvedJavaType) : result;
-                return result;
-            }
-
-            private void maybeEagerlyResolve(int cpi, int bytecode) {
-                if (graphBuilderConfig.eagerResolving() || intrinsicContext != null) {
-                    constantPool.loadReferencedType(cpi, bytecode);
-                }
-            }
-
-            private JavaTypeProfile getProfileForTypeCheck(ResolvedJavaType type) {
-                if (parsingIntrinsic() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) {
-                    return null;
-                } else {
-                    return profilingInfo.getTypeProfile(bci());
-                }
-            }
-
-            private void genCheckCast() {
-                int cpi = getStream().readCPI();
-                JavaType type = lookupType(cpi, CHECKCAST);
-                ValueNode object = frameState.pop(Kind.Object);
-
-                if (!(type instanceof ResolvedJavaType)) {
-                    handleUnresolvedCheckCast(type, object);
-                    return;
-                }
-                ResolvedJavaType resolvedType = (ResolvedJavaType) type;
-                JavaTypeProfile profile = getProfileForTypeCheck(resolvedType);
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleCheckCast(this, object, resolvedType, profile)) {
-                        return;
-                    }
-                }
-
-                ValueNode checkCastNode = null;
-                if (profile != null) {
-                    if (profile.getNullSeen().isFalse()) {
-                        object = append(GuardingPiNode.createNullCheck(object));
-                        ResolvedJavaType singleType = profile.asSingleType();
-                        if (singleType != null) {
-                            LogicNode typeCheck = append(TypeCheckNode.create(singleType, object));
-                            if (typeCheck.isTautology()) {
-                                checkCastNode = object;
-                            } else {
-                                GuardingPiNode piNode = append(new GuardingPiNode(object, typeCheck, false, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile,
-                                                StampFactory.exactNonNull(singleType)));
-                                checkCastNode = piNode;
-                            }
-                        }
-                    }
-                }
-                if (checkCastNode == null) {
-                    checkCastNode = append(createCheckCast(resolvedType, object, profile, false));
-                }
-                frameState.push(Kind.Object, checkCastNode);
-            }
-
-            private void genInstanceOf() {
-                int cpi = getStream().readCPI();
-                JavaType type = lookupType(cpi, INSTANCEOF);
-                ValueNode object = frameState.pop(Kind.Object);
-
-                if (!(type instanceof ResolvedJavaType)) {
-                    handleUnresolvedInstanceOf(type, object);
-                    return;
-                }
-                ResolvedJavaType resolvedType = (ResolvedJavaType) type;
-                JavaTypeProfile profile = getProfileForTypeCheck(resolvedType);
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleInstanceOf(this, object, resolvedType, profile)) {
-                        return;
-                    }
-                }
-
-                ValueNode instanceOfNode = null;
-                if (profile != null) {
-                    if (profile.getNullSeen().isFalse()) {
-                        object = append(GuardingPiNode.createNullCheck(object));
-                        ResolvedJavaType singleType = profile.asSingleType();
-                        if (singleType != null) {
-                            LogicNode typeCheck = append(TypeCheckNode.create(singleType, object));
-                            append(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile));
-                            instanceOfNode = LogicConstantNode.forBoolean(resolvedType.isAssignableFrom(singleType));
-                        }
-                    }
-                }
-                if (instanceOfNode == null) {
-                    instanceOfNode = createInstanceOf(resolvedType, object, profile);
-                }
-                frameState.push(Kind.Int, append(genConditional(genUnique(instanceOfNode))));
-            }
-
-            void genNewInstance(int cpi) {
-                JavaType type = lookupType(cpi, NEW);
-
-                if (!(type instanceof ResolvedJavaType) || !((ResolvedJavaType) type).isInitialized()) {
-                    handleUnresolvedNewInstance(type);
-                    return;
-                }
-                ResolvedJavaType resolvedType = (ResolvedJavaType) type;
-
-                ResolvedJavaType[] skippedExceptionTypes = this.graphBuilderConfig.getSkippedExceptionTypes();
-                if (skippedExceptionTypes != null) {
-                    for (ResolvedJavaType exceptionType : skippedExceptionTypes) {
-                        if (exceptionType.isAssignableFrom(resolvedType)) {
-                            append(new DeoptimizeNode(DeoptimizationAction.None, TransferToInterpreter));
-                            return;
-                        }
-                    }
-                }
-
-                frameState.push(Kind.Object, append(createNewInstance(resolvedType, true)));
-            }
-
-            private void genNewPrimitiveArray(int typeCode) {
-                ResolvedJavaType elementType = metaAccess.lookupJavaType(arrayTypeCodeToClass(typeCode));
-                ValueNode length = frameState.pop(Kind.Int);
-                frameState.push(Kind.Object, append(createNewArray(elementType, length, true)));
-            }
-
-            private void genNewObjectArray(int cpi) {
-                JavaType type = lookupType(cpi, ANEWARRAY);
-                ValueNode length = frameState.pop(Kind.Int);
-
-                if (!(type instanceof ResolvedJavaType)) {
-                    handleUnresolvedNewObjectArray(type, length);
-                    return;
-                }
-                ResolvedJavaType resolvedType = (ResolvedJavaType) type;
-
-                frameState.push(Kind.Object, append(createNewArray(resolvedType, length, true)));
-            }
-
-            private void genNewMultiArray(int cpi) {
-                JavaType type = lookupType(cpi, MULTIANEWARRAY);
-                int rank = getStream().readUByte(bci() + 3);
-                List<ValueNode> dims = new ArrayList<>(Collections.nCopies(rank, null));
-                for (int i = rank - 1; i >= 0; i--) {
-                    dims.set(i, frameState.pop(Kind.Int));
-                }
-
-                if (!(type instanceof ResolvedJavaType)) {
-                    handleUnresolvedNewMultiArray(type, dims);
-                    return;
-                }
-                ResolvedJavaType resolvedType = (ResolvedJavaType) type;
-
-                frameState.push(Kind.Object, append(createNewMultiArray(resolvedType, dims)));
-            }
-
-            private void genGetField(JavaField field) {
-                ValueNode receiver = emitExplicitExceptions(frameState.pop(Kind.Object), null);
-
-                if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
-                    handleUnresolvedLoadField(field, receiver);
-                    return;
-                }
-                ResolvedJavaField resolvedField = (ResolvedJavaField) field;
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleLoadField(this, receiver, resolvedField)) {
-                        return;
-                    }
-                }
-
-                frameState.push(field.getKind(), append(genLoadField(receiver, resolvedField)));
-            }
-
-            /**
-             * @param receiver the receiver of an object based operation
-             * @param index the index of an array based operation that is to be tested for out of
-             *            bounds. This is null for a non-array operation.
-             * @return the receiver value possibly modified to have a tighter stamp
-             */
-            protected ValueNode emitExplicitExceptions(ValueNode receiver, ValueNode index) {
-                assert receiver != null;
-                if (graphBuilderConfig.omitAllExceptionEdges() ||
-                                profilingInfo == null ||
-                                (optimisticOpts.useExceptionProbabilityForOperations() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE && !GraalOptions.StressExplicitExceptionCode.getValue())) {
-                    return receiver;
-                }
-
-                ValueNode nonNullReceiver = emitExplicitNullCheck(receiver);
-                if (index != null) {
-                    ValueNode length = append(genArrayLength(nonNullReceiver));
-                    emitExplicitBoundsCheck(index, length);
-                }
-                EXPLICIT_EXCEPTIONS.increment();
-                return nonNullReceiver;
-            }
-
-            private void genPutField(JavaField field) {
-                ValueNode value = frameState.pop(field.getKind());
-                ValueNode receiver = emitExplicitExceptions(frameState.pop(Kind.Object), null);
-
-                if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
-                    handleUnresolvedStoreField(field, value, receiver);
-                    return;
-                }
-                ResolvedJavaField resolvedField = (ResolvedJavaField) field;
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleStoreField(this, receiver, resolvedField, value)) {
-                        return;
-                    }
-                }
-
-                genStoreField(receiver, resolvedField, value);
-            }
-
-            private void genGetStatic(JavaField field) {
-                if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-                    handleUnresolvedLoadField(field, null);
-                    return;
-                }
-                ResolvedJavaField resolvedField = (ResolvedJavaField) field;
-
-                /*
-                 * Javac does not allow use of "$assertionsDisabled" for a field name but Eclipse
-                 * does, in which case a suffix is added to the generated field.
-                 */
-                if ((parsingIntrinsic() || graphBuilderConfig.omitAssertions()) && resolvedField.isSynthetic() && resolvedField.getName().startsWith("$assertionsDisabled")) {
-                    frameState.push(field.getKind(), ConstantNode.forBoolean(true, graph));
-                    return;
-                }
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleLoadStaticField(this, resolvedField)) {
-                        return;
-                    }
-                }
-
-                frameState.push(field.getKind(), append(genLoadField(null, resolvedField)));
-            }
-
-            private void genPutStatic(JavaField field) {
-                ValueNode value = frameState.pop(field.getKind());
-                if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-                    handleUnresolvedStoreField(field, value, null);
-                    return;
-                }
-                ResolvedJavaField resolvedField = (ResolvedJavaField) field;
-
-                for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) {
-                    if (plugin.handleStoreStaticField(this, resolvedField, value)) {
-                        return;
-                    }
-                }
-
-                genStoreField(null, resolvedField, value);
-            }
-
-            private double[] switchProbability(int numberOfCases, int bci) {
-                double[] prob = (profilingInfo == null ? null : 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 void genSwitch(BytecodeSwitch bs) {
-                int bci = bci();
-                ValueNode value = frameState.pop(Kind.Int);
-
-                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;
-                boolean constantValue = value.isConstant();
-                for (int i = 0; i < nofCases + 1; i++) {
-                    if (i < nofCases) {
-                        keys[i] = bs.keyAt(i);
-                    }
-
-                    if (!constantValue && 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;
-                    }
-                }
-
-                genIntegerSwitch(value, actualSuccessors, keys, keyProbabilities, keySuccessors);
-
-            }
-
-            protected boolean isNeverExecutedCode(double probability) {
-                return probability == 0 && optimisticOpts.removeNeverExecutedCode();
-            }
-
-            protected double branchProbability() {
-                if (profilingInfo == null) {
-                    return 0.5;
-                }
-                assert assertAtIfBytecode();
-                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;
-                    }
-                }
-                return probability;
-            }
-
-            private boolean assertAtIfBytecode() {
-                int bytecode = stream.currentBC();
-                switch (bytecode) {
-                    case IFEQ:
-                    case IFNE:
-                    case IFLT:
-                    case IFGE:
-                    case IFGT:
-                    case IFLE:
-                    case IF_ICMPEQ:
-                    case IF_ICMPNE:
-                    case IF_ICMPLT:
-                    case IF_ICMPGE:
-                    case IF_ICMPGT:
-                    case IF_ICMPLE:
-                    case IF_ACMPEQ:
-                    case IF_ACMPNE:
-                    case IFNULL:
-                    case IFNONNULL:
-                        return true;
-                }
-                assert false : String.format("%x is not an if bytecode", bytecode);
-                return true;
-            }
-
-            public final void processBytecode(int bci, int opcode) {
-                int cpi;
-
-                // Checkstyle: stop
-                // @formatter:off
-                switch (opcode) {
-                    case NOP            : /* nothing to do */ break;
-                    case ACONST_NULL    : frameState.push(Kind.Object, appendConstant(JavaConstant.NULL_POINTER)); break;
-                    case ICONST_M1      : // fall through
-                    case ICONST_0       : // fall through
-                    case ICONST_1       : // fall through
-                    case ICONST_2       : // fall through
-                    case ICONST_3       : // fall through
-                    case ICONST_4       : // fall through
-                    case ICONST_5       : frameState.push(Kind.Int, appendConstant(JavaConstant.forInt(opcode - ICONST_0))); break;
-                    case LCONST_0       : // fall through
-                    case LCONST_1       : frameState.push(Kind.Long, appendConstant(JavaConstant.forLong(opcode - LCONST_0))); break;
-                    case FCONST_0       : // fall through
-                    case FCONST_1       : // fall through
-                    case FCONST_2       : frameState.push(Kind.Float, appendConstant(JavaConstant.forFloat(opcode - FCONST_0))); break;
-                    case DCONST_0       : // fall through
-                    case DCONST_1       : frameState.push(Kind.Double, appendConstant(JavaConstant.forDouble(opcode - DCONST_0))); break;
-                    case BIPUSH         : frameState.push(Kind.Int, appendConstant(JavaConstant.forInt(stream.readByte()))); break;
-                    case SIPUSH         : frameState.push(Kind.Int, appendConstant(JavaConstant.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           : frameState.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(getStream(), bci())); break;
-                    case IRETURN        : genReturn(frameState.pop(Kind.Int), Kind.Int); break;
-                    case LRETURN        : genReturn(frameState.pop(Kind.Long), Kind.Long); break;
-                    case FRETURN        : genReturn(frameState.pop(Kind.Float), Kind.Float); break;
-                    case DRETURN        : genReturn(frameState.pop(Kind.Double), Kind.Double); break;
-                    case ARETURN        : genReturn(frameState.pop(Kind.Object), Kind.Object); break;
-                    case RETURN         : genReturn(null, Kind.Void); 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.pop(Kind.Object), stream.nextBCI()); break;
-                    case MONITOREXIT    : genMonitorExit(frameState.pop(Kind.Object), null, stream.nextBCI()); 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 %d (%s) [bci=%d]", opcode, nameOf(opcode), bci);
-                }
-                // @formatter:on
-                // Checkstyle: resume
-            }
-
-            private void genArrayLength() {
-                frameState.push(Kind.Int, append(genArrayLength(frameState.pop(Kind.Object))));
-            }
-
-            public ResolvedJavaMethod getMethod() {
-                return method;
-            }
-
-            public FrameStateBuilder getFrameStateBuilder() {
-                return frameState;
-            }
-
-            protected boolean traceInstruction(int bci, int opcode, boolean blockStart) {
-                if (Debug.isEnabled() && Options.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) {
-                    traceInstructionHelper(bci, opcode, blockStart);
-                }
-                return true;
-            }
-
-            private void traceInstructionHelper(int bci, int opcode, boolean blockStart) {
-                StringBuilder sb = new StringBuilder(40);
-                sb.append(blockStart ? '+' : '|');
-                if (bci < 10) {
-                    sb.append("  ");
-                } else if (bci < 100) {
-                    sb.append(' ');
-                }
-                sb.append(bci).append(": ").append(Bytecodes.nameOf(opcode));
-                for (int i = bci + 1; i < stream.nextBCI(); ++i) {
-                    sb.append(' ').append(stream.readUByte(i));
-                }
-                if (!currentBlock.getJsrScope().isEmpty()) {
-                    sb.append(' ').append(currentBlock.getJsrScope());
-                }
-                Debug.log("%s", sb);
-            }
-
-            public boolean parsingIntrinsic() {
-                return intrinsicContext != null;
-            }
-
-            public BytecodeParser getNonIntrinsicAncestor() {
-                BytecodeParser ancestor = parent;
-                while (ancestor != null && ancestor.parsingIntrinsic()) {
-                    ancestor = ancestor.parent;
-                }
-                return ancestor;
-            }
+        /* Hook for subclasses of Instance to provide a subclass of BytecodeParser. */
+        protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
+            return new BytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
         }
     }
-
-    static String nSpaces(int n) {
-        return n == 0 ? "" : format("%" + n + "s", "");
-    }
-
-    @SuppressWarnings("all")
-    private static boolean assertionsEnabled() {
-        boolean assertionsEnabled = false;
-        assert assertionsEnabled = true;
-        return assertionsEnabled;
-    }
 }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Fri May 29 19:11:39 2015 -0700
@@ -131,10 +131,6 @@
         inputNodesCount = statistics.inputNodesCount;
     }
 
-    protected CharSequence getDetailedName() {
-        return getName();
-    }
-
     public final void apply(final StructuredGraph graph, final C context) {
         apply(graph, context, true);
     }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java	Fri May 29 19:11:39 2015 -0700
@@ -23,7 +23,7 @@
 package com.oracle.graal.replacements;
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
 
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.nodes.*;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Fri May 29 19:11:39 2015 -0700
@@ -22,7 +22,7 @@
  */
 package com.oracle.graal.replacements;
 
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
 import static com.oracle.jvmci.common.JVMCIError.*;
 
 import java.util.*;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Fri May 29 17:01:31 2015 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Fri May 29 19:11:39 2015 -0700
@@ -24,7 +24,7 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+import static com.oracle.graal.java.BytecodeParser.Options.*;
 import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*;
 import static com.oracle.jvmci.meta.MetaUtil.*;
 import static java.lang.String.*;