diff graal/com.oracle.max.graal.compiler/src/com/sun/c1x/graph/GraphBuilder.java @ 2872:0341b6424579

Project renaming.
author Thomas Wuerthinger <thomas@wuerthinger.net>
date Wed, 08 Jun 2011 08:42:25 +0200
parents graal/GraalCompiler/src/com/sun/c1x/graph/GraphBuilder.java@fc75fd3fa5e4
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.max.graal.compiler/src/com/sun/c1x/graph/GraphBuilder.java	Wed Jun 08 08:42:25 2011 +0200
@@ -0,0 +1,1521 @@
+/*
+ * Copyright (c) 2009, 2011, 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.sun.c1x.graph;
+
+import static com.sun.cri.bytecode.Bytecodes.*;
+import static java.lang.reflect.Modifier.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.max.graal.schedule.*;
+import com.sun.c1x.*;
+import com.sun.c1x.debug.*;
+import com.sun.c1x.graph.BlockMap.*;
+import com.sun.c1x.graph.BlockMap.Block;
+import com.sun.c1x.ir.*;
+import com.sun.c1x.util.*;
+import com.sun.c1x.value.*;
+import com.sun.cri.bytecode.*;
+import com.sun.cri.ci.*;
+import com.sun.cri.ri.*;
+import com.sun.cri.ri.RiType.*;
+
+/**
+ * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph.
+ * A number of optimizations may be performed during parsing of the bytecode, including value
+ * numbering, inlining, constant folding, strength reduction, etc.
+ */
+public final class GraphBuilder {
+
+    /**
+     * The minimum value to which {@link C1XOptions#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 C1XOptions#TraceBytecodeParserLevel} must be set to trace
+     * the frame state before each bytecode instruction as it is parsed.
+     */
+    public static final int TRACELEVEL_STATE = 2;
+
+    private final C1XCompilation compilation;
+    private final CompilerGraph graph;
+
+    private final CiStatistics stats;
+    private final RiRuntime runtime;
+    private final RiMethod method;
+    private final RiConstantPool constantPool;
+
+    private final BytecodeStream stream;           // the bytecode stream
+    private final LogStream log;
+    private final FrameStateBuilder frameState;          // the current execution state
+
+    // bci-to-block mapping
+    private Block[] blockFromBci;
+    private ArrayList<Block> blockList;
+
+    private int nextBlockNumber;
+
+    private Value methodSynchronizedObject;
+    private CiExceptionHandler syncHandler;
+
+    private Block unwindBlock;
+    private Block returnBlock;
+
+    // the worklist of blocks, sorted by depth first number
+    private final PriorityQueue<Block> workList = new PriorityQueue<Block>(10, new Comparator<Block>() {
+        public int compare(Block o1, Block o2) {
+            return o1.blockID - o2.blockID;
+        }
+    });
+
+    private Instruction lastInstr;                 // the last instruction added
+
+    private final Set<Block> blocksOnWorklist = new HashSet<Block>();
+    private final Set<Block> blocksVisited = new HashSet<Block>();
+
+
+    /**
+     * Creates a new, initialized, {@code GraphBuilder} instance for a given compilation.
+     *
+     * @param compilation the compilation
+     * @param ir the IR to build the graph into
+     * @param graph
+     */
+    public GraphBuilder(C1XCompilation compilation, RiMethod method, CompilerGraph graph) {
+        this.compilation = compilation;
+        this.graph = graph;
+
+        this.runtime = compilation.runtime;
+        this.method = method;
+        this.stats = compilation.stats;
+        this.log = C1XOptions.TraceBytecodeParserLevel > 0 ? new LogStream(TTY.out()) : null;
+        this.stream = new BytecodeStream(method.code());
+
+        this.constantPool = runtime.getConstantPool(method);
+        this.frameState = new FrameStateBuilder(method, graph);
+    }
+
+    /**
+     * Builds the graph for a the specified {@code IRScope}.
+     *
+     * @param createUnwind setting this to true will always generate an unwind block, even if there is no exception
+     *            handler and the method is not synchronized
+     */
+    public void build(boolean createUnwind) {
+        if (log != null) {
+            log.println();
+            log.println("Compiling " + method);
+        }
+
+        // 2. compute the block map, setup exception handlers and get the entrypoint(s)
+        BlockMap blockMap = compilation.getBlockMap(method);
+
+        blockList = new ArrayList<Block>(blockMap.blocks);
+        blockFromBci = new Block[method.code().length];
+        for (int i = 0; i < blockList.size(); i++) {
+            int blockID = nextBlockNumber();
+            assert blockID == i;
+            Block block = blockList.get(i);
+            if (block.startBci >= 0) {
+                blockFromBci[block.startBci] = block;
+            }
+        }
+
+        // 1. create the start block
+        Block startBlock = nextBlock(Instruction.SYNCHRONIZATION_ENTRY_BCI);
+        markOnWorkList(startBlock);
+        lastInstr = createTarget(startBlock, frameState);
+        graph.start().setStart(lastInstr);
+
+        if (isSynchronized(method.accessFlags())) {
+            // 4A.1 add a monitor enter to the start block
+            methodSynchronizedObject = synchronizedObject(frameState, method);
+            genMonitorEnter(methodSynchronizedObject, Instruction.SYNCHRONIZATION_ENTRY_BCI);
+            // 4A.2 finish the start block
+            finishStartBlock(startBlock);
+
+            // 4A.3 setup an exception handler to unlock the root method synchronized object
+            syncHandler = new CiExceptionHandler(0, method.code().length, Instruction.SYNCHRONIZATION_ENTRY_BCI, 0, null);
+        } else {
+            // 4B.1 simply finish the start block
+            finishStartBlock(startBlock);
+
+            if (createUnwind) {
+                syncHandler = new CiExceptionHandler(0, method.code().length, Instruction.SYNCHRONIZATION_ENTRY_BCI, 0, null);
+            }
+        }
+
+        // 5. SKIPPED: look for intrinsics
+
+        // 6B.1 do the normal parsing
+        addToWorkList(blockFromBci[0]);
+        iterateAllBlocks();
+
+        // remove Placeholders
+        for (Node n : graph.getNodes()) {
+            if (n instanceof Placeholder) {
+                Placeholder p = (Placeholder) n;
+                assert p.blockPredecessors().size() == 1;
+                Node pred = p.blockPredecessors().get(0);
+                int predIndex = p.predecessorsIndex().get(0);
+                pred.successors().setAndClear(predIndex, p, 0);
+                p.delete();
+            }
+        }
+
+        // remove FrameStates
+        for (Node n : graph.getNodes()) {
+            if (n instanceof FrameState) {
+                boolean delete = false;
+                if (n.usages().size() == 0 && n.predecessors().size() == 0) {
+                    delete = true;
+                }
+                if (delete) {
+                    n.delete();
+                }
+            }
+        }
+    }
+
+    private int nextBlockNumber() {
+        stats.blockCount++;
+        return nextBlockNumber++;
+    }
+
+    private Block nextBlock(int bci) {
+        Block block = new Block();
+        block.startBci = bci;
+        block.endBci = bci;
+        block.blockID = nextBlockNumber();
+        return block;
+    }
+
+    private Block unwindBlock() {
+        if (unwindBlock == null) {
+            unwindBlock = new Block();
+            unwindBlock.startBci = Instruction.SYNCHRONIZATION_ENTRY_BCI;
+            unwindBlock.endBci = Instruction.SYNCHRONIZATION_ENTRY_BCI;
+            unwindBlock.blockID = nextBlockNumber();
+            addToWorkList(unwindBlock);
+        }
+        return unwindBlock;
+    }
+
+    private Block returnBlock() {
+        if (returnBlock == null) {
+            returnBlock = new Block();
+            returnBlock.startBci = Instruction.SYNCHRONIZATION_ENTRY_BCI;
+            returnBlock.endBci = Instruction.SYNCHRONIZATION_ENTRY_BCI;
+            returnBlock.blockID = nextBlockNumber();
+            addToWorkList(returnBlock);
+        }
+        return returnBlock;
+    }
+
+    private void markOnWorkList(Block block) {
+        blocksOnWorklist.add(block);
+    }
+
+    private boolean isOnWorkList(Block block) {
+        return blocksOnWorklist.contains(block);
+    }
+
+    private void markVisited(Block block) {
+        blocksVisited.add(block);
+    }
+
+    private boolean isVisited(Block block) {
+        return blocksVisited.contains(block);
+    }
+
+    private void finishStartBlock(Block startBlock) {
+        assert bci() == 0;
+        Instruction target = createTargetAt(0, frameState);
+        appendGoto(target);
+    }
+
+    public void mergeOrClone(Block target, FrameStateAccess newState) {
+        Instruction first = target.firstInstruction;
+        if (target.isLoopHeader && isVisited(target)) {
+            first = ((LoopBegin) first).loopEnd();
+        }
+        assert first instanceof StateSplit;
+
+        int bci = target.startBci;
+
+        FrameState existingState = ((StateSplit) first).stateBefore();
+
+        if (existingState == null) {
+            // copy state because it is modified
+            FrameState duplicate = newState.duplicate(bci);
+
+            // if the block is a loop header, insert all necessary phis
+            if (first instanceof LoopBegin && target.isLoopHeader) {
+                assert first instanceof Merge;
+                insertLoopPhis((Merge) first, duplicate);
+                ((Merge) first).setStateBefore(duplicate);
+            } else {
+                ((StateSplit) first).setStateBefore(duplicate);
+            }
+        } else {
+            if (!C1XOptions.AssumeVerifiedBytecode && !existingState.isCompatibleWith(newState)) {
+                // stacks or locks do not match--bytecodes would not verify
+                TTY.println(existingState.toString());
+                TTY.println(newState.duplicate(0).toString());
+                throw new CiBailout("stack or locks do not match");
+            }
+            assert existingState.localsSize() == newState.localsSize();
+            assert existingState.stackSize() == newState.stackSize();
+
+            if (first instanceof Placeholder) {
+                assert !target.isLoopHeader;
+                Merge merge = new Merge(graph);
+
+                Placeholder p = (Placeholder) first;
+                assert p.next() == null;
+                p.replace(merge);
+                target.firstInstruction = merge;
+                merge.setStateBefore(existingState);
+                first = merge;
+            }
+
+            existingState.merge((Merge) first, newState);
+        }
+
+        for (int j = 0; j < frameState.localsSize() + frameState.stackSize(); ++j) {
+            if (frameState.valueAt(j) != null) {
+                assert !frameState.valueAt(j).isDeleted();
+            }
+        }
+    }
+
+    private void insertLoopPhis(Merge merge, FrameState newState) {
+        int stackSize = newState.stackSize();
+        for (int i = 0; i < stackSize; i++) {
+            // always insert phis for the stack
+            Value x = newState.stackAt(i);
+            if (x != null) {
+                newState.setupPhiForStack(merge, i).addInput(x);
+            }
+        }
+        int localsSize = newState.localsSize();
+        for (int i = 0; i < localsSize; i++) {
+            Value x = newState.localAt(i);
+            if (x != null) {
+                newState.setupPhiForLocal(merge, i).addInput(x);
+            }
+        }
+    }
+
+    public BytecodeStream stream() {
+        return stream;
+    }
+
+    public int bci() {
+        return stream.currentBCI();
+    }
+
+    private void loadLocal(int index, CiKind kind) {
+        frameState.push(kind, frameState.loadLocal(index));
+    }
+
+    private void storeLocal(CiKind kind, int index) {
+        frameState.storeLocal(index, frameState.pop(kind));
+    }
+
+    public boolean covers(RiExceptionHandler handler, int bci) {
+        return handler.startBCI() <= bci && bci < handler.endBCI();
+    }
+
+    public boolean isCatchAll(RiExceptionHandler handler) {
+        return handler.catchTypeCPI() == 0;
+    }
+
+    private Instruction handleException(Value exceptionObject, int bci) {
+        assert bci == Instruction.SYNCHRONIZATION_ENTRY_BCI || bci == bci() : "invalid bci";
+
+        RiExceptionHandler firstHandler = null;
+        RiExceptionHandler[] exceptionHandlers = method.exceptionHandlers();
+        // join with all potential exception handlers
+        if (exceptionHandlers != null) {
+            for (RiExceptionHandler handler : exceptionHandlers) {
+                // if the handler covers this bytecode index, add it to the list
+                if (covers(handler, bci)) {
+                    firstHandler = handler;
+                    break;
+                }
+            }
+        }
+
+        if (firstHandler == null) {
+            firstHandler = syncHandler;
+        }
+
+        if (firstHandler != null) {
+            compilation.setHasExceptionHandlers();
+
+            Block dispatchBlock = null;
+            for (Block block : blockList) {
+                if (block instanceof ExceptionBlock) {
+                    ExceptionBlock excBlock = (ExceptionBlock) block;
+                    if (excBlock.handler == firstHandler) {
+                        dispatchBlock = block;
+                        break;
+                    }
+                }
+            }
+            // if there's no dispatch block then the catch block needs to be a catch all
+            if (dispatchBlock == null) {
+                assert isCatchAll(firstHandler);
+                int handlerBCI = firstHandler.handlerBCI();
+                if (handlerBCI == Instruction.SYNCHRONIZATION_ENTRY_BCI) {
+                    dispatchBlock = unwindBlock();
+                } else {
+                    dispatchBlock = blockFromBci[handlerBCI];
+                }
+            }
+            FrameState entryState = frameState.duplicateWithEmptyStack(bci);
+
+            StateSplit entry = new Placeholder(graph);
+            entry.setStateBefore(entryState);
+
+            Instruction currentNext = entry;
+            Value currentExceptionObject = exceptionObject;
+            if (currentExceptionObject == null) {
+                ExceptionObject exception = new ExceptionObject(graph);
+                entry.setNext(exception);
+                currentNext = exception;
+                currentExceptionObject = exception;
+            }
+            FrameState stateWithException = entryState.duplicateModified(bci, CiKind.Void, currentExceptionObject);
+
+            Instruction successor = createTarget(dispatchBlock, stateWithException);
+            currentNext.setNext(successor);
+            return entry;
+        }
+        return null;
+    }
+
+    private void genLoadConstant(int cpi) {
+        Object con = constantPool.lookupConstant(cpi);
+
+        if (con instanceof RiType) {
+            // this is a load of class constant which might be unresolved
+            RiType riType = (RiType) con;
+            if (!riType.isResolved()) {
+                append(new Deoptimize(graph));
+                frameState.push(CiKind.Object, append(Constant.forObject(null, graph)));
+            } else {
+                frameState.push(CiKind.Object, append(new Constant(riType.getEncoding(Representation.JavaClass), graph)));
+            }
+        } else if (con instanceof CiConstant) {
+            CiConstant constant = (CiConstant) con;
+            frameState.push(constant.kind.stackKind(), appendConstant(constant));
+        } else {
+            throw new Error("lookupConstant returned an object of incorrect type");
+        }
+    }
+
+    private void genLoadIndexed(CiKind kind) {
+        Value index = frameState.ipop();
+        Value array = frameState.apop();
+        Value length = append(new ArrayLength(array, graph));
+        Value v = append(new LoadIndexed(array, index, length, kind, graph));
+        frameState.push(kind.stackKind(), v);
+    }
+
+    private void genStoreIndexed(CiKind kind) {
+        Value value = frameState.pop(kind.stackKind());
+        Value index = frameState.ipop();
+        Value array = frameState.apop();
+        Value length = append(new ArrayLength(array, graph));
+        StoreIndexed result = new StoreIndexed(array, index, length, kind, value, graph);
+        append(result);
+    }
+
+    private void stackOp(int opcode) {
+        switch (opcode) {
+            case POP: {
+                frameState.xpop();
+                break;
+            }
+            case POP2: {
+                frameState.xpop();
+                frameState.xpop();
+                break;
+            }
+            case DUP: {
+                Value w = frameState.xpop();
+                frameState.xpush(w);
+                frameState.xpush(w);
+                break;
+            }
+            case DUP_X1: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                frameState.xpush(w1);
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                break;
+            }
+            case DUP_X2: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                Value w3 = frameState.xpop();
+                frameState.xpush(w1);
+                frameState.xpush(w3);
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                break;
+            }
+            case DUP2: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                break;
+            }
+            case DUP2_X1: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                Value w3 = frameState.xpop();
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                frameState.xpush(w3);
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                break;
+            }
+            case DUP2_X2: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                Value w3 = frameState.xpop();
+                Value w4 = frameState.xpop();
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                frameState.xpush(w4);
+                frameState.xpush(w3);
+                frameState.xpush(w2);
+                frameState.xpush(w1);
+                break;
+            }
+            case SWAP: {
+                Value w1 = frameState.xpop();
+                Value w2 = frameState.xpop();
+                frameState.xpush(w1);
+                frameState.xpush(w2);
+                break;
+            }
+            default:
+                throw Util.shouldNotReachHere();
+        }
+
+    }
+
+    private void genArithmeticOp(CiKind kind, int opcode) {
+        genArithmeticOp(kind, opcode, false);
+    }
+
+    private void genArithmeticOp(CiKind kind, int opcode, boolean canTrap) {
+        genArithmeticOp(kind, opcode, kind, kind, canTrap);
+    }
+
+    private void genArithmeticOp(CiKind result, int opcode, CiKind x, CiKind y, boolean canTrap) {
+        Value yValue = frameState.pop(y);
+        Value xValue = frameState.pop(x);
+        Value result1 = append(new Arithmetic(opcode, result, xValue, yValue, isStrict(method.accessFlags()), canTrap, graph));
+        if (canTrap) {
+            append(new ValueAnchor(result1, graph));
+        }
+        frameState.push(result, result1);
+    }
+
+    private void genNegateOp(CiKind kind) {
+        frameState.push(kind, append(new Negate(frameState.pop(kind), graph)));
+    }
+
+    private void genShiftOp(CiKind kind, int opcode) {
+        Value s = frameState.ipop();
+        Value x = frameState.pop(kind);
+        Shift v;
+        switch(opcode){
+            case ISHL:
+            case LSHL: v = new LeftShift(kind, x, s, graph); break;
+            case ISHR:
+            case LSHR: v = new RightShift(kind, x, s, graph); break;
+            case IUSHR:
+            case LUSHR: v = new UnsignedRightShift(kind, x, s, graph); break;
+            default:
+                throw new CiBailout("should not reach");
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genLogicOp(CiKind kind, int opcode) {
+        Value y = frameState.pop(kind);
+        Value x = frameState.pop(kind);
+        Logic v;
+        switch(opcode){
+            case IAND:
+            case LAND: v = new And(kind, x, y, graph); break;
+            case IOR:
+            case LOR: v = new Or(kind, x, y, graph); break;
+            case IXOR:
+            case LXOR: v = new Xor(kind, x, y, graph); break;
+            default:
+                throw new CiBailout("should not reach");
+        }
+        frameState.push(kind, append(v));
+    }
+
+    private void genCompareOp(CiKind kind, int opcode, CiKind resultKind) {
+        Value y = frameState.pop(kind);
+        Value x = frameState.pop(kind);
+        Value value = append(new NormalizeCompare(opcode, resultKind, x, y, graph));
+        if (!resultKind.isVoid()) {
+            frameState.ipush(value);
+        }
+    }
+
+    private void genConvert(int opcode, CiKind from, CiKind to) {
+        CiKind tt = to.stackKind();
+        frameState.push(tt, append(new Convert(opcode, frameState.pop(from.stackKind()), tt, graph)));
+    }
+
+    private void genIncrement() {
+        int index = stream().readLocalIndex();
+        int delta = stream().readIncrement();
+        Value x = frameState.localAt(index);
+        Value y = append(Constant.forInt(delta, graph));
+        frameState.storeLocal(index, append(new Arithmetic(IADD, CiKind.Int, x, y, isStrict(method.accessFlags()), false, graph)));
+    }
+
+    private void genGoto(int fromBCI, int toBCI) {
+        appendGoto(createTargetAt(toBCI, frameState));
+    }
+
+    private void ifNode(Value x, Condition cond, Value y) {
+        assert !x.isDeleted() && !y.isDeleted();
+        If ifNode = new If(new Compare(x, cond, y, graph), graph);
+        append(ifNode);
+        Instruction tsucc = createTargetAt(stream().readBranchDest(), frameState);
+        ifNode.setBlockSuccessor(0, tsucc);
+        Instruction fsucc = createTargetAt(stream().nextBCI(), frameState);
+        ifNode.setBlockSuccessor(1, fsucc);
+    }
+
+    private void genIfZero(Condition cond) {
+        Value y = appendConstant(CiConstant.INT_0);
+        Value x = frameState.ipop();
+        ifNode(x, cond, y);
+    }
+
+    private void genIfNull(Condition cond) {
+        Value y = appendConstant(CiConstant.NULL_OBJECT);
+        Value x = frameState.apop();
+        ifNode(x, cond, y);
+    }
+
+    private void genIfSame(CiKind kind, Condition cond) {
+        Value y = frameState.pop(kind);
+        Value x = frameState.pop(kind);
+        assert !x.isDeleted() && !y.isDeleted();
+        ifNode(x, cond, y);
+    }
+
+    private void genThrow(int bci) {
+        Value exception = frameState.apop();
+        append(new NullCheck(exception, graph));
+
+        Instruction entry = handleException(exception, bci);
+        if (entry != null) {
+            append(entry);
+        } else {
+            frameState.clearStack();
+            frameState.apush(exception);
+            appendGoto(createTarget(unwindBlock(), frameState));
+        }
+    }
+
+    private void genCheckCast() {
+        int cpi = stream().readCPI();
+        RiType type = constantPool.lookupType(cpi, CHECKCAST);
+        boolean isInitialized = type.isResolved();
+        Value typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, isInitialized, cpi);
+        Value object = frameState.apop();
+        if (typeInstruction != null) {
+            frameState.apush(append(new CheckCast(type, typeInstruction, object, graph)));
+        } else {
+            frameState.apush(appendConstant(CiConstant.NULL_OBJECT));
+        }
+    }
+
+    private void genInstanceOf() {
+        int cpi = stream().readCPI();
+        RiType type = constantPool.lookupType(cpi, INSTANCEOF);
+        boolean isInitialized = type.isResolved();
+        Value typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, isInitialized, cpi);
+        Value object = frameState.apop();
+        if (typeInstruction != null) {
+            frameState.ipush(append(new InstanceOf(type, typeInstruction, object, graph)));
+        } else {
+            frameState.ipush(appendConstant(CiConstant.INT_0));
+        }
+    }
+
+    void genNewInstance(int cpi) {
+        RiType type = constantPool.lookupType(cpi, NEW);
+        if (type.isResolved()) {
+            NewInstance n = new NewInstance(type, cpi, constantPool, graph);
+            frameState.apush(append(n));
+        } else {
+            append(new Deoptimize(graph));
+            frameState.apush(appendConstant(CiConstant.NULL_OBJECT));
+        }
+    }
+
+    private void genNewTypeArray(int typeCode) {
+        CiKind kind = CiKind.fromArrayTypeCode(typeCode);
+        RiType elementType = runtime.asRiType(kind);
+        NewTypeArray nta = new NewTypeArray(frameState.ipop(), elementType, graph);
+        frameState.apush(append(nta));
+    }
+
+    private void genNewObjectArray(int cpi) {
+        RiType type = constantPool.lookupType(cpi, ANEWARRAY);
+        Value length = frameState.ipop();
+        if (type.isResolved()) {
+            NewArray n = new NewObjectArray(type, length, graph);
+            frameState.apush(append(n));
+        } else {
+            append(new Deoptimize(graph));
+            frameState.apush(appendConstant(CiConstant.NULL_OBJECT));
+        }
+
+    }
+
+    private void genNewMultiArray(int cpi) {
+        RiType type = constantPool.lookupType(cpi, MULTIANEWARRAY);
+        int rank = stream().readUByte(bci() + 3);
+        Value[] dims = new Value[rank];
+        for (int i = rank - 1; i >= 0; i--) {
+            dims[i] = frameState.ipop();
+        }
+        if (type.isResolved()) {
+            NewArray n = new NewMultiArray(type, dims, cpi, constantPool, graph);
+            frameState.apush(append(n));
+        } else {
+            append(new Deoptimize(graph));
+            frameState.apush(appendConstant(CiConstant.NULL_OBJECT));
+        }
+    }
+
+    private void genGetField(int cpi, RiField field) {
+        CiKind kind = field.kind();
+        Value receiver = frameState.apop();
+        if (field.isResolved()) {
+            LoadField load = new LoadField(receiver, field, graph);
+            appendOptimizedLoadField(kind, load);
+        } else {
+            append(new Deoptimize(graph));
+            frameState.push(kind.stackKind(), append(Constant.defaultForKind(kind, graph)));
+        }
+    }
+
+    private void genPutField(int cpi, RiField field) {
+        Value value = frameState.pop(field.kind().stackKind());
+        Value receiver = frameState.apop();
+        if (field.isResolved()) {
+            StoreField store = new StoreField(receiver, field, value, graph);
+            appendOptimizedStoreField(store);
+        } else {
+            append(new Deoptimize(graph));
+        }
+    }
+
+    private void genGetStatic(int cpi, RiField field) {
+        RiType holder = field.holder();
+        boolean isInitialized = field.isResolved();
+        CiConstant constantValue = null;
+        if (isInitialized) {
+            constantValue = field.constantValue(null);
+        }
+        if (constantValue != null) {
+            frameState.push(constantValue.kind.stackKind(), appendConstant(constantValue));
+        } else {
+            Value container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, isInitialized, cpi);
+            CiKind kind = field.kind();
+            if (container != null) {
+                LoadField load = new LoadField(container, field, graph);
+                appendOptimizedLoadField(kind, load);
+            } else {
+                append(new Deoptimize(graph));
+                frameState.push(kind.stackKind(), append(Constant.defaultForKind(kind, graph)));
+            }
+        }
+    }
+
+    private void genPutStatic(int cpi, RiField field) {
+        RiType holder = field.holder();
+        Value container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, field.isResolved(), cpi);
+        Value value = frameState.pop(field.kind().stackKind());
+        if (container != null) {
+            StoreField store = new StoreField(container, field, value, graph);
+            appendOptimizedStoreField(store);
+        } else {
+            append(new Deoptimize(graph));
+        }
+    }
+
+    private Value genTypeOrDeopt(RiType.Representation representation, RiType holder, boolean initialized, int cpi) {
+        if (initialized) {
+            return appendConstant(holder.getEncoding(representation));
+        } else {
+            append(new Deoptimize(graph));
+            return null;
+        }
+    }
+
+    private void appendOptimizedStoreField(StoreField store) {
+        append(store);
+    }
+
+    private void appendOptimizedLoadField(CiKind kind, LoadField load) {
+        // append the load to the instruction
+        Value optimized = append(load);
+        frameState.push(kind.stackKind(), optimized);
+    }
+
+    private void genInvokeStatic(RiMethod target, int cpi, RiConstantPool constantPool) {
+        RiType holder = target.holder();
+        boolean isInitialized = target.isResolved() && holder.isInitialized();
+        if (!isInitialized && C1XOptions.ResolveClassBeforeStaticInvoke) {
+            // Re-use the same resolution code as for accessing a static field. Even though
+            // the result of resolution is not used by the invocation (only the side effect
+            // of initialization is required), it can be commoned with static field accesses.
+            genTypeOrDeopt(RiType.Representation.StaticFields, holder, isInitialized, cpi);
+        }
+        Value[] args = frameState.popArguments(target.signature().argumentSlots(false));
+        appendInvoke(INVOKESTATIC, target, args, cpi, constantPool);
+    }
+
+    private void genInvokeInterface(RiMethod target, int cpi, RiConstantPool constantPool) {
+        Value[] args = frameState.popArguments(target.signature().argumentSlots(true));
+        genInvokeIndirect(INVOKEINTERFACE, target, args, cpi, constantPool);
+
+    }
+
+    private void genInvokeVirtual(RiMethod target, int cpi, RiConstantPool constantPool) {
+        Value[] args = frameState.popArguments(target.signature().argumentSlots(true));
+        genInvokeIndirect(INVOKEVIRTUAL, target, args, cpi, constantPool);
+
+    }
+
+    private void genInvokeSpecial(RiMethod target, RiType knownHolder, int cpi, RiConstantPool constantPool) {
+        Value[] args = frameState.popArguments(target.signature().argumentSlots(true));
+        invokeDirect(target, args, knownHolder, cpi, constantPool);
+
+    }
+
+    private void genInvokeIndirect(int opcode, RiMethod target, Value[] args, int cpi, RiConstantPool constantPool) {
+        Value receiver = args[0];
+        // attempt to devirtualize the call
+        if (target.isResolved()) {
+            RiType klass = target.holder();
+
+            // 0. check for trivial cases
+            if (target.canBeStaticallyBound() && !isAbstract(target.accessFlags())) {
+                // check for trivial cases (e.g. final methods, nonvirtual methods)
+                invokeDirect(target, args, target.holder(), cpi, constantPool);
+                return;
+            }
+            // 1. check if the exact type of the receiver can be determined
+            RiType exact = getExactType(klass, receiver);
+            if (exact != null && exact.isResolved()) {
+                // either the holder class is exact, or the receiver object has an exact type
+                invokeDirect(exact.resolveMethodImpl(target), args, exact, cpi, constantPool);
+                return;
+            }
+        }
+        // devirtualization failed, produce an actual invokevirtual
+        appendInvoke(opcode, target, args, cpi, constantPool);
+    }
+
+    private CiKind returnKind(RiMethod target) {
+        return target.signature().returnKind();
+    }
+
+    private void invokeDirect(RiMethod target, Value[] args, RiType knownHolder, int cpi, RiConstantPool constantPool) {
+        appendInvoke(INVOKESPECIAL, target, args, cpi, constantPool);
+    }
+
+    private void appendInvoke(int opcode, RiMethod target, Value[] args, int cpi, RiConstantPool constantPool) {
+        CiKind resultType = returnKind(target);
+        Invoke invoke = new Invoke(bci(), opcode, resultType.stackKind(), args, target, target.signature().returnType(method.holder()), graph);
+        Value result = appendWithBCI(invoke);
+        invoke.setExceptionEdge(handleException(null, bci()));
+        frameState.pushReturn(resultType, result);
+    }
+
+    private RiType getExactType(RiType staticType, Value receiver) {
+        RiType exact = staticType.exactType();
+        if (exact == null) {
+            exact = receiver.exactType();
+            if (exact == null) {
+                if (receiver.isConstant()) {
+                    exact = runtime.getTypeOf(receiver.asConstant());
+                }
+                if (exact == null) {
+                    RiType declared = receiver.declaredType();
+                    exact = declared == null || !declared.isResolved() ? null : declared.exactType();
+                }
+            }
+        }
+        return exact;
+    }
+
+    private void callRegisterFinalizer() {
+        Value receiver = frameState.loadLocal(0);
+        RiType declaredType = receiver.declaredType();
+        RiType receiverType = declaredType;
+        RiType exactType = receiver.exactType();
+        if (exactType == null && declaredType != null) {
+            exactType = declaredType.exactType();
+        }
+        if (exactType == null && receiver instanceof Local && ((Local) receiver).index() == 0) {
+            // the exact type isn't known, but the receiver is parameter 0 => use holder
+            receiverType = method.holder();
+            exactType = receiverType.exactType();
+        }
+        boolean needsCheck = true;
+        if (exactType != null) {
+            // we have an exact type
+            needsCheck = exactType.hasFinalizer();
+        } else {
+            // if either the declared type of receiver or the holder can be assumed to have no finalizers
+            if (declaredType != null && !declaredType.hasFinalizableSubclass()) {
+                if (compilation.recordNoFinalizableSubclassAssumption(declaredType)) {
+                    needsCheck = false;
+                }
+            }
+
+            if (receiverType != null && !receiverType.hasFinalizableSubclass()) {
+                if (compilation.recordNoFinalizableSubclassAssumption(receiverType)) {
+                    needsCheck = false;
+                }
+            }
+        }
+
+        if (needsCheck) {
+            // append a call to the finalizer registration
+            append(new RegisterFinalizer(frameState.loadLocal(0), frameState.create(bci()), graph));
+            C1XMetrics.InlinedFinalizerChecks++;
+        }
+    }
+
+    private void genReturn(Value x) {
+        frameState.clearStack();
+        if (x != null) {
+            frameState.push(x.kind, x);
+        }
+        appendGoto(createTarget(returnBlock(), frameState));
+    }
+
+    private void genMonitorEnter(Value x, int bci) {
+        int lockNumber = frameState.locksSize();
+        MonitorAddress lockAddress = null;
+        if (runtime.sizeOfBasicObjectLock() != 0) {
+            lockAddress = new MonitorAddress(lockNumber, graph);
+            append(lockAddress);
+        }
+        MonitorEnter monitorEnter = new MonitorEnter(x, lockAddress, lockNumber, graph);
+        appendWithBCI(monitorEnter);
+        frameState.lock(x);
+        if (bci == Instruction.SYNCHRONIZATION_ENTRY_BCI) {
+            monitorEnter.setStateAfter(frameState.create(0));
+        }
+    }
+
+    private void genMonitorExit(Value x) {
+        int lockNumber = frameState.locksSize() - 1;
+        if (lockNumber < 0) {
+            throw new CiBailout("monitor stack underflow");
+        }
+        MonitorAddress lockAddress = null;
+        if (runtime.sizeOfBasicObjectLock() != 0) {
+            lockAddress = new MonitorAddress(lockNumber, graph);
+            append(lockAddress);
+        }
+        appendWithBCI(new MonitorExit(x, lockAddress, lockNumber, graph));
+        frameState.unlock();
+    }
+
+    private void genJsr(int dest) {
+        throw new CiBailout("jsr/ret not supported");
+    }
+
+    private void genRet(int localIndex) {
+        throw new CiBailout("jsr/ret not supported");
+    }
+
+    private void genTableswitch() {
+        int bci = bci();
+        Value value = frameState.ipop();
+        BytecodeTableSwitch ts = new BytecodeTableSwitch(stream(), bci);
+        int max = ts.numberOfCases();
+        List<Instruction> list = new ArrayList<Instruction>(max + 1);
+        List<Integer> offsetList = new ArrayList<Integer>(max + 1);
+        for (int i = 0; i < max; i++) {
+            // add all successors to the successor list
+            int offset = ts.offsetAt(i);
+            list.add(null);
+            offsetList.add(offset);
+        }
+        int offset = ts.defaultOffset();
+        list.add(null);
+        offsetList.add(offset);
+        TableSwitch tableSwitch = new TableSwitch(value, list, ts.lowKey(), graph);
+        for (int i = 0; i < offsetList.size(); ++i) {
+            tableSwitch.setBlockSuccessor(i, createTargetAt(bci + offsetList.get(i), frameState));
+        }
+        append(tableSwitch);
+    }
+
+    private void genLookupswitch() {
+        int bci = bci();
+        Value value = frameState.ipop();
+        BytecodeLookupSwitch ls = new BytecodeLookupSwitch(stream(), bci);
+        int max = ls.numberOfCases();
+        List<Instruction> list = new ArrayList<Instruction>(max + 1);
+        List<Integer> offsetList = new ArrayList<Integer>(max + 1);
+        int[] keys = new int[max];
+        for (int i = 0; i < max; i++) {
+            // add all successors to the successor list
+            int offset = ls.offsetAt(i);
+            list.add(null);
+            offsetList.add(offset);
+            keys[i] = ls.keyAt(i);
+        }
+        int offset = ls.defaultOffset();
+        list.add(null);
+        offsetList.add(offset);
+        LookupSwitch lookupSwitch = new LookupSwitch(value, list, keys, graph);
+        for (int i = 0; i < offsetList.size(); ++i) {
+            lookupSwitch.setBlockSuccessor(i, createTargetAt(bci + offsetList.get(i), frameState));
+        }
+        append(lookupSwitch);
+    }
+
+    private Value appendConstant(CiConstant constant) {
+        return append(new Constant(constant, graph));
+    }
+
+    private Value append(Instruction x) {
+        return appendWithBCI(x);
+    }
+
+    private Value append(Value v) {
+        return v;
+    }
+
+    private Value appendWithBCI(Instruction x) {
+        assert x.predecessors().size() == 0 : "instruction should not have been appended yet";
+        assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")";
+        lastInstr.setNext(x);
+
+        lastInstr = x;
+        if (++stats.nodeCount >= C1XOptions.MaximumInstructionCount) {
+            // bailout if we've exceeded the maximum inlining size
+            throw new CiBailout("Method and/or inlining is too large");
+        }
+
+        return x;
+    }
+
+    private Instruction createTargetAt(int bci, FrameStateAccess stateAfter) {
+        return createTarget(blockFromBci[bci], stateAfter);
+    }
+
+    private Instruction createTarget(Block block, FrameStateAccess stateAfter) {
+        assert block != null && stateAfter != null;
+        assert block.isLoopHeader || block.firstInstruction == null || block.firstInstruction.next() == null : "non-loop block must be iterated after all its predecessors";
+
+        if (block.isExceptionEntry) {
+            assert stateAfter.stackSize() == 1;
+        }
+
+        if (block.firstInstruction == null) {
+            if (block.isLoopHeader) {
+//                block.firstInstruction = new Merge(block.startBci, graph);
+
+                LoopBegin loopBegin = new LoopBegin(graph);
+                LoopEnd loopEnd = new LoopEnd(graph);
+                loopEnd.setLoopBegin(loopBegin);
+                block.firstInstruction = loopBegin;
+            } else {
+                block.firstInstruction = new Placeholder(graph);
+            }
+        }
+        mergeOrClone(block, stateAfter);
+        addToWorkList(block);
+
+        if (block.firstInstruction instanceof LoopBegin && isVisited(block)) {
+            return ((LoopBegin) block.firstInstruction).loopEnd();
+        } else {
+            return block.firstInstruction;
+        }
+    }
+
+    private Value synchronizedObject(FrameStateAccess state, RiMethod target) {
+        if (isStatic(target.accessFlags())) {
+            Constant classConstant = new Constant(target.holder().getEncoding(Representation.JavaClass), graph);
+            return append(classConstant);
+        } else {
+            return state.localAt(0);
+        }
+    }
+
+    private void iterateAllBlocks() {
+        Block block;
+        while ((block = removeFromWorkList()) != null) {
+
+            // remove blocks that have no predecessors by the time it their bytecodes are parsed
+            if (block.firstInstruction == null) {
+                markVisited(block);
+                continue;
+            }
+
+            if (!isVisited(block)) {
+                markVisited(block);
+                // now parse the block
+                frameState.initializeFrom(((StateSplit) block.firstInstruction).stateBefore());
+                lastInstr = block.firstInstruction;
+                assert block.firstInstruction.next() == null : "instructions already appended at block " + block.blockID;
+
+                if (block == returnBlock) {
+                    createReturnBlock(block);
+                } else if (block == unwindBlock) {
+                    createUnwindBlock(block);
+                } else if (block instanceof ExceptionBlock) {
+                    createExceptionDispatch((ExceptionBlock) block);
+                } else {
+                    iterateBytecodesForBlock(block);
+                }
+            }
+        }
+        for (Block b : blocksVisited) {
+            if (b.isLoopHeader) {
+                LoopBegin begin = (LoopBegin) b.firstInstruction;
+                LoopEnd end = begin.loopEnd();
+
+//              This can happen with degenerated loops like this one:
+//                for (;;) {
+//                    try {
+//                        break;
+//                    } catch (UnresolvedException iioe) {
+//                    }
+//                }
+                if (end.stateBefore() != null) {
+                    begin.stateBefore().merge(begin, end.stateBefore());
+                } else {
+                    end.delete();
+                    Merge merge = new Merge(graph);
+                    merge.successors().setAndClear(merge.nextIndex(), begin, begin.nextIndex());
+                    begin.replace(merge);
+                }
+            }
+        }
+    }
+
+    private void createUnwindBlock(Block block) {
+        if (Modifier.isSynchronized(method.accessFlags())) {
+            genMonitorExit(methodSynchronizedObject);
+        }
+        append(graph.createUnwind(frameState.apop()));
+    }
+
+    private void createReturnBlock(Block block) {
+        if (method.isConstructor() && method.holder().superType() == null) {
+            callRegisterFinalizer();
+        }
+        CiKind returnKind = method.signature().returnKind().stackKind();
+        Value x = returnKind == CiKind.Void ? null : frameState.pop(returnKind);
+        assert frameState.stackSize() == 0;
+
+        if (Modifier.isSynchronized(method.accessFlags())) {
+            genMonitorExit(methodSynchronizedObject);
+        }
+        append(graph.createReturn(x));
+    }
+
+    private void createExceptionDispatch(ExceptionBlock block) {
+        if (block.handler == null) {
+            assert frameState.stackSize() == 1 : "only exception object expected on stack, actual size: " + frameState.stackSize();
+            createUnwindBlock(block);
+        } else {
+            assert frameState.stackSize() == 1;
+
+            Block nextBlock = block.next == null ? unwindBlock() : block.next;
+            if (block.handler.catchType().isResolved()) {
+                Instruction catchSuccessor = createTarget(blockFromBci[block.handler.handlerBCI()], frameState);
+                Instruction nextDispatch = createTarget(nextBlock, frameState);
+                append(new ExceptionDispatch(frameState.stackAt(0), catchSuccessor, nextDispatch, block.handler.catchType(), graph));
+            } else {
+                Deoptimize deopt = new Deoptimize(graph);
+                deopt.setMessage("unresolved " + block.handler.catchType().name());
+                append(deopt);
+                Instruction nextDispatch = createTarget(nextBlock, frameState);
+                appendGoto(nextDispatch);
+            }
+        }
+    }
+
+    private void appendGoto(Instruction target) {
+        lastInstr.setNext(target);
+    }
+
+    private void iterateBytecodesForBlock(Block block) {
+        assert frameState != null;
+
+        stream.setBCI(block.startBci);
+
+        int endBCI = stream.endBCI();
+        boolean blockStart = true;
+
+        int bci = block.startBci;
+        while (bci < endBCI) {
+            Block nextBlock = blockFromBci[bci];
+            if (nextBlock != null && nextBlock != block) {
+                assert !nextBlock.isExceptionEntry;
+                // we fell through to the next block, add a goto and break
+                appendGoto(createTarget(nextBlock, frameState));
+                break;
+            }
+            // read the opcode
+            int opcode = stream.currentBC();
+
+            traceState();
+            traceInstruction(bci, opcode, blockStart);
+            processBytecode(bci, opcode);
+
+            if (Schedule.isBlockEnd(lastInstr) || lastInstr.next() != null) {
+                break;
+            }
+
+            stream.next();
+            bci = stream.currentBCI();
+            if (lastInstr instanceof StateSplit) {
+                StateSplit stateSplit = (StateSplit) lastInstr;
+                if (stateSplit.stateAfter() == null && stateSplit.needsStateAfter()) {
+                    stateSplit.setStateAfter(frameState.create(bci));
+                }
+            }
+            blockStart = false;
+        }
+    }
+
+    private void traceState() {
+        if (C1XOptions.TraceBytecodeParserLevel >= TRACELEVEL_STATE && !TTY.isSuppressed()) {
+            log.println(String.format("|   state [nr locals = %d, stack depth = %d, method = %s]", frameState.localsSize(), frameState.stackSize(), method));
+            for (int i = 0; i < frameState.localsSize(); ++i) {
+                Value value = frameState.localAt(i);
+                log.println(String.format("|   local[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind.javaName, value));
+            }
+            for (int i = 0; i < frameState.stackSize(); ++i) {
+                Value value = frameState.stackAt(i);
+                log.println(String.format("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind.javaName, value));
+            }
+            for (int i = 0; i < frameState.locksSize(); ++i) {
+                Value value = frameState.lockAt(i);
+                log.println(String.format("|   lock[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind.javaName, value));
+            }
+        }
+    }
+
+    private void processBytecode(int bci, int opcode) {
+        int cpi;
+
+        // Checkstyle: stop
+        switch (opcode) {
+            case NOP            : /* nothing to do */ break;
+            case ACONST_NULL    : frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); break;
+            case ICONST_M1      : frameState.ipush(appendConstant(CiConstant.INT_MINUS_1)); break;
+            case ICONST_0       : frameState.ipush(appendConstant(CiConstant.INT_0)); break;
+            case ICONST_1       : frameState.ipush(appendConstant(CiConstant.INT_1)); break;
+            case ICONST_2       : frameState.ipush(appendConstant(CiConstant.INT_2)); break;
+            case ICONST_3       : frameState.ipush(appendConstant(CiConstant.INT_3)); break;
+            case ICONST_4       : frameState.ipush(appendConstant(CiConstant.INT_4)); break;
+            case ICONST_5       : frameState.ipush(appendConstant(CiConstant.INT_5)); break;
+            case LCONST_0       : frameState.lpush(appendConstant(CiConstant.LONG_0)); break;
+            case LCONST_1       : frameState.lpush(appendConstant(CiConstant.LONG_1)); break;
+            case FCONST_0       : frameState.fpush(appendConstant(CiConstant.FLOAT_0)); break;
+            case FCONST_1       : frameState.fpush(appendConstant(CiConstant.FLOAT_1)); break;
+            case FCONST_2       : frameState.fpush(appendConstant(CiConstant.FLOAT_2)); break;
+            case DCONST_0       : frameState.dpush(appendConstant(CiConstant.DOUBLE_0)); break;
+            case DCONST_1       : frameState.dpush(appendConstant(CiConstant.DOUBLE_1)); break;
+            case BIPUSH         : frameState.ipush(appendConstant(CiConstant.forInt(stream.readByte()))); break;
+            case SIPUSH         : frameState.ipush(appendConstant(CiConstant.forInt(stream.readShort()))); break;
+            case LDC            : // fall through
+            case LDC_W          : // fall through
+            case LDC2_W         : genLoadConstant(stream.readCPI()); break;
+            case ILOAD          : loadLocal(stream.readLocalIndex(), CiKind.Int); break;
+            case LLOAD          : loadLocal(stream.readLocalIndex(), CiKind.Long); break;
+            case FLOAD          : loadLocal(stream.readLocalIndex(), CiKind.Float); break;
+            case DLOAD          : loadLocal(stream.readLocalIndex(), CiKind.Double); break;
+            case ALOAD          : loadLocal(stream.readLocalIndex(), CiKind.Object); break;
+            case ILOAD_0        : // fall through
+            case ILOAD_1        : // fall through
+            case ILOAD_2        : // fall through
+            case ILOAD_3        : loadLocal(opcode - ILOAD_0, CiKind.Int); break;
+            case LLOAD_0        : // fall through
+            case LLOAD_1        : // fall through
+            case LLOAD_2        : // fall through
+            case LLOAD_3        : loadLocal(opcode - LLOAD_0, CiKind.Long); break;
+            case FLOAD_0        : // fall through
+            case FLOAD_1        : // fall through
+            case FLOAD_2        : // fall through
+            case FLOAD_3        : loadLocal(opcode - FLOAD_0, CiKind.Float); break;
+            case DLOAD_0        : // fall through
+            case DLOAD_1        : // fall through
+            case DLOAD_2        : // fall through
+            case DLOAD_3        : loadLocal(opcode - DLOAD_0, CiKind.Double); break;
+            case ALOAD_0        : // fall through
+            case ALOAD_1        : // fall through
+            case ALOAD_2        : // fall through
+            case ALOAD_3        : loadLocal(opcode - ALOAD_0, CiKind.Object); break;
+            case IALOAD         : genLoadIndexed(CiKind.Int   ); break;
+            case LALOAD         : genLoadIndexed(CiKind.Long  ); break;
+            case FALOAD         : genLoadIndexed(CiKind.Float ); break;
+            case DALOAD         : genLoadIndexed(CiKind.Double); break;
+            case AALOAD         : genLoadIndexed(CiKind.Object); break;
+            case BALOAD         : genLoadIndexed(CiKind.Byte  ); break;
+            case CALOAD         : genLoadIndexed(CiKind.Char  ); break;
+            case SALOAD         : genLoadIndexed(CiKind.Short ); break;
+            case ISTORE         : storeLocal(CiKind.Int, stream.readLocalIndex()); break;
+            case LSTORE         : storeLocal(CiKind.Long, stream.readLocalIndex()); break;
+            case FSTORE         : storeLocal(CiKind.Float, stream.readLocalIndex()); break;
+            case DSTORE         : storeLocal(CiKind.Double, stream.readLocalIndex()); break;
+            case ASTORE         : storeLocal(CiKind.Object, stream.readLocalIndex()); break;
+            case ISTORE_0       : // fall through
+            case ISTORE_1       : // fall through
+            case ISTORE_2       : // fall through
+            case ISTORE_3       : storeLocal(CiKind.Int, opcode - ISTORE_0); break;
+            case LSTORE_0       : // fall through
+            case LSTORE_1       : // fall through
+            case LSTORE_2       : // fall through
+            case LSTORE_3       : storeLocal(CiKind.Long, opcode - LSTORE_0); break;
+            case FSTORE_0       : // fall through
+            case FSTORE_1       : // fall through
+            case FSTORE_2       : // fall through
+            case FSTORE_3       : storeLocal(CiKind.Float, opcode - FSTORE_0); break;
+            case DSTORE_0       : // fall through
+            case DSTORE_1       : // fall through
+            case DSTORE_2       : // fall through
+            case DSTORE_3       : storeLocal(CiKind.Double, opcode - DSTORE_0); break;
+            case ASTORE_0       : // fall through
+            case ASTORE_1       : // fall through
+            case ASTORE_2       : // fall through
+            case ASTORE_3       : storeLocal(CiKind.Object, opcode - ASTORE_0); break;
+            case IASTORE        : genStoreIndexed(CiKind.Int   ); break;
+            case LASTORE        : genStoreIndexed(CiKind.Long  ); break;
+            case FASTORE        : genStoreIndexed(CiKind.Float ); break;
+            case DASTORE        : genStoreIndexed(CiKind.Double); break;
+            case AASTORE        : genStoreIndexed(CiKind.Object); break;
+            case BASTORE        : genStoreIndexed(CiKind.Byte  ); break;
+            case CASTORE        : genStoreIndexed(CiKind.Char  ); break;
+            case SASTORE        : genStoreIndexed(CiKind.Short ); break;
+            case POP            : // fall through
+            case POP2           : // fall through
+            case DUP            : // fall through
+            case DUP_X1         : // fall through
+            case DUP_X2         : // fall through
+            case DUP2           : // fall through
+            case DUP2_X1        : // fall through
+            case DUP2_X2        : // fall through
+            case SWAP           : stackOp(opcode); break;
+            case IADD           : // fall through
+            case ISUB           : // fall through
+            case IMUL           : genArithmeticOp(CiKind.Int, opcode); break;
+            case IDIV           : // fall through
+            case IREM           : genArithmeticOp(CiKind.Int, opcode, true); break;
+            case LADD           : // fall through
+            case LSUB           : // fall through
+            case LMUL           : genArithmeticOp(CiKind.Long, opcode); break;
+            case LDIV           : // fall through
+            case LREM           : genArithmeticOp(CiKind.Long, opcode, true); break;
+            case FADD           : // fall through
+            case FSUB           : // fall through
+            case FMUL           : // fall through
+            case FDIV           : // fall through
+            case FREM           : genArithmeticOp(CiKind.Float, opcode); break;
+            case DADD           : // fall through
+            case DSUB           : // fall through
+            case DMUL           : // fall through
+            case DDIV           : // fall through
+            case DREM           : genArithmeticOp(CiKind.Double, opcode); break;
+            case INEG           : genNegateOp(CiKind.Int); break;
+            case LNEG           : genNegateOp(CiKind.Long); break;
+            case FNEG           : genNegateOp(CiKind.Float); break;
+            case DNEG           : genNegateOp(CiKind.Double); break;
+            case ISHL           : // fall through
+            case ISHR           : // fall through
+            case IUSHR          : genShiftOp(CiKind.Int, opcode); break;
+            case IAND           : // fall through
+            case IOR            : // fall through
+            case IXOR           : genLogicOp(CiKind.Int, opcode); break;
+            case LSHL           : // fall through
+            case LSHR           : // fall through
+            case LUSHR          : genShiftOp(CiKind.Long, opcode); break;
+            case LAND           : // fall through
+            case LOR            : // fall through
+            case LXOR           : genLogicOp(CiKind.Long, opcode); break;
+            case IINC           : genIncrement(); break;
+            case I2L            : genConvert(opcode, CiKind.Int   , CiKind.Long  ); break;
+            case I2F            : genConvert(opcode, CiKind.Int   , CiKind.Float ); break;
+            case I2D            : genConvert(opcode, CiKind.Int   , CiKind.Double); break;
+            case L2I            : genConvert(opcode, CiKind.Long  , CiKind.Int   ); break;
+            case L2F            : genConvert(opcode, CiKind.Long  , CiKind.Float ); break;
+            case L2D            : genConvert(opcode, CiKind.Long  , CiKind.Double); break;
+            case F2I            : genConvert(opcode, CiKind.Float , CiKind.Int   ); break;
+            case F2L            : genConvert(opcode, CiKind.Float , CiKind.Long  ); break;
+            case F2D            : genConvert(opcode, CiKind.Float , CiKind.Double); break;
+            case D2I            : genConvert(opcode, CiKind.Double, CiKind.Int   ); break;
+            case D2L            : genConvert(opcode, CiKind.Double, CiKind.Long  ); break;
+            case D2F            : genConvert(opcode, CiKind.Double, CiKind.Float ); break;
+            case I2B            : genConvert(opcode, CiKind.Int   , CiKind.Byte  ); break;
+            case I2C            : genConvert(opcode, CiKind.Int   , CiKind.Char  ); break;
+            case I2S            : genConvert(opcode, CiKind.Int   , CiKind.Short ); break;
+            case LCMP           : genCompareOp(CiKind.Long, opcode, CiKind.Int); break;
+            case FCMPL          : genCompareOp(CiKind.Float, opcode, CiKind.Int); break;
+            case FCMPG          : genCompareOp(CiKind.Float, opcode, CiKind.Int); break;
+            case DCMPL          : genCompareOp(CiKind.Double, opcode, CiKind.Int); break;
+            case DCMPG          : genCompareOp(CiKind.Double, opcode, CiKind.Int); 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(CiKind.Int, Condition.EQ); break;
+            case IF_ICMPNE      : genIfSame(CiKind.Int, Condition.NE); break;
+            case IF_ICMPLT      : genIfSame(CiKind.Int, Condition.LT); break;
+            case IF_ICMPGE      : genIfSame(CiKind.Int, Condition.GE); break;
+            case IF_ICMPGT      : genIfSame(CiKind.Int, Condition.GT); break;
+            case IF_ICMPLE      : genIfSame(CiKind.Int, Condition.LE); break;
+            case IF_ACMPEQ      : genIfSame(frameState.peekKind(), Condition.EQ); break;
+            case IF_ACMPNE      : genIfSame(frameState.peekKind(), Condition.NE); break;
+            case GOTO           : genGoto(stream.currentBCI(), stream.readBranchDest()); break;
+            case JSR            : genJsr(stream.readBranchDest()); break;
+            case RET            : genRet(stream.readLocalIndex()); break;
+            case TABLESWITCH    : genTableswitch(); break;
+            case LOOKUPSWITCH   : genLookupswitch(); break;
+            case IRETURN        : genReturn(frameState.ipop()); break;
+            case LRETURN        : genReturn(frameState.lpop()); break;
+            case FRETURN        : genReturn(frameState.fpop()); break;
+            case DRETURN        : genReturn(frameState.dpop()); break;
+            case ARETURN        : genReturn(frameState.apop()); break;
+            case RETURN         : genReturn(null  ); break;
+            case GETSTATIC      : cpi = stream.readCPI(); genGetStatic(cpi, constantPool.lookupField(cpi, opcode)); break;
+            case PUTSTATIC      : cpi = stream.readCPI(); genPutStatic(cpi, constantPool.lookupField(cpi, opcode)); break;
+            case GETFIELD       : cpi = stream.readCPI(); genGetField(cpi, constantPool.lookupField(cpi, opcode)); break;
+            case PUTFIELD       : cpi = stream.readCPI(); genPutField(cpi, constantPool.lookupField(cpi, opcode)); break;
+            case INVOKEVIRTUAL  : cpi = stream.readCPI(); genInvokeVirtual(constantPool.lookupMethod(cpi, opcode), cpi, constantPool); break;
+            case INVOKESPECIAL  : cpi = stream.readCPI(); genInvokeSpecial(constantPool.lookupMethod(cpi, opcode), null, cpi, constantPool); break;
+            case INVOKESTATIC   : cpi = stream.readCPI(); genInvokeStatic(constantPool.lookupMethod(cpi, opcode), cpi, constantPool); break;
+            case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(constantPool.lookupMethod(cpi, opcode), cpi, constantPool); break;
+            case NEW            : genNewInstance(stream.readCPI()); break;
+            case NEWARRAY       : genNewTypeArray(stream.readLocalIndex()); break;
+            case ANEWARRAY      : genNewObjectArray(stream.readCPI()); break;
+            case ARRAYLENGTH    : genArrayLength(); break;
+            case ATHROW         : genThrow(stream.currentBCI()); break;
+            case CHECKCAST      : genCheckCast(); break;
+            case INSTANCEOF     : genInstanceOf(); break;
+            case MONITORENTER   : genMonitorEnter(frameState.apop(), stream.currentBCI()); break;
+            case MONITOREXIT    : genMonitorExit(frameState.apop()); break;
+            case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break;
+            case IFNULL         : genIfNull(Condition.EQ); break;
+            case IFNONNULL      : genIfNull(Condition.NE); break;
+            case GOTO_W         : genGoto(stream.currentBCI(), stream.readFarBranchDest()); break;
+            case JSR_W          : genJsr(stream.readFarBranchDest()); break;
+            case BREAKPOINT:
+                throw new CiBailout("concurrent setting of breakpoint");
+            default:
+                throw new CiBailout("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]");
+        }
+        // Checkstyle: resume
+    }
+
+    private void traceInstruction(int bci, int opcode, boolean blockStart) {
+        if (C1XOptions.TraceBytecodeParserLevel >= TRACELEVEL_INSTRUCTIONS && !TTY.isSuppressed()) {
+            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));
+            }
+            log.println(sb.toString());
+        }
+    }
+
+    private void genArrayLength() {
+        frameState.ipush(append(new ArrayLength(frameState.apop(), graph)));
+    }
+
+    /**
+     * Adds a block to the worklist, if it is not already in the worklist.
+     * This method will keep the worklist topologically stored (i.e. the lower
+     * DFNs are earlier in the list).
+     * @param block the block to add to the work list
+     */
+    private void addToWorkList(Block block) {
+        if (!isOnWorkList(block)) {
+            markOnWorkList(block);
+            sortIntoWorkList(block);
+        }
+    }
+
+    private void sortIntoWorkList(Block top) {
+        workList.offer(top);
+    }
+
+    /**
+     * Removes the next block from the worklist. The list is sorted topologically, so the
+     * block with the lowest depth first number in the list will be removed and returned.
+     * @return the next block from the worklist; {@code null} if there are no blocks
+     * in the worklist
+     */
+    private Block removeFromWorkList() {
+        return workList.poll();
+    }
+}