changeset 5260:4e4a868c2b2a

Merge.
author Doug Simon <doug.simon@oracle.com>
date Fri, 20 Apr 2012 14:14:42 +0200
parents af8958fe5a3a (current diff) 1e153fdac9fb (diff)
children 6cd293b125ea 6b2d030d01ff
files
diffstat 3 files changed, 227 insertions(+), 347 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Fri Apr 20 14:13:59 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Fri Apr 20 14:14:42 2012 +0200
@@ -35,84 +35,34 @@
 import com.oracle.max.cri.ri.*;
 
 /**
- * Builds a mapping between bytecodes and basic blocks and builds a conservative control flow
- * graph. Note that this class serves a similar role to C1's {@code BlockListBuilder}, but makes fewer assumptions about
- * what the compiler interface provides. It builds all basic blocks for the control flow graph without requiring the
- * compiler interface to provide a bitmap of the beginning of basic blocks. It makes two linear passes; one over the
- * bytecodes to build block starts and successor lists, and one pass over the block map to build the CFG.
- *
- * Note that the CFG built by this class is <i>not</i> connected to the actual {@code BlockBegin} instances; this class
- * does, however, compute and assign the reverse postorder number of the blocks. This comment needs refinement. (MJJ)
- *
- * <H2>More Details on {@link BciBlockMapping#build}</H2>
- *
- * If the method has any exception handlers the {@linkplain #exceptionMap exception map} will be created (TBD).
- *
- * The bytecodes are then scanned linearly looking for bytecodes that contain control transfers, e.g., {@code GOTO},
- * {@code RETURN}, {@code IFGE}, and creating the corresponding entries in {@link #successorMap} and {@link #blockMap}.
- * In addition, if {@link #exceptionMap} is not null, entries are made for any bytecode that can cause an exception.
- * More TBD.
- *
- * Observe that this process finds bytecodes that terminate basic blocks, so the {@link #moveSuccessorLists} method is
- * called to reassign the successors to the {@code BlockBegin} node that actually starts the block.
- *
- * <H3>Example</H3>
- *
- * Consider the following source code:
- *
- * <pre>
- * <code>
- *     public static int test(int arg1, int arg2) {
- *         int x = 0;
- *         while (arg2 > 0) {
- *             if (arg1 > 0) {
- *                 x += 1;
- *             } else if (arg1 < 0) {
- *                 x -= 1;
- *             }
- *         }
- *         return x;
- *     }
- * </code>
- * </pre>
- *
- * This is translated by javac to the following bytecode:
- *
- * <pre>
- * <code>
- *    0:   iconst_0
- *    1:   istore_2
- *    2:   goto    22
- *    5:   iload_0
- *    6:   ifle    15
- *    9:   iinc    2, 1
- *    12:  goto    22
- *    15:  iload_0
- *    16:  ifge    22
- *    19:  iinc    2, -1
- *    22:  iload_1
- *    23:  ifgt    5
- *    26:  iload_2
- *    27:  ireturn
- *    </code>
- * </pre>
- *
- * There are seven basic blocks in this method, 0..2, 5..6, 9..12, 15..16, 19..19, 22..23 and 26..27. Therefore, before
- * the call to {@code moveSuccessorLists}, the {@code blockMap} array has {@code BlockBegin} nodes at indices 0, 5, 9,
- * 15, 19, 22 and 26. The {@code successorMap} array has entries at 2, 6, 12, 16, 23, 27 corresponding to the control
- * transfer bytecodes. The entry at index 6, for example, is a length two array of {@code BlockBegin} nodes for indices
- * 9 and 15, which are the successors for the basic block 5..6. After the call to {@code moveSuccessors}, {@code
- * successorMap} has entries at 0, 5, 9, 15, 19, 22 and 26, i.e, matching {@code blockMap}.
- * <p>
- * Next the blocks are numbered using <a href="http://en.wikipedia.org/wiki/Depth-first_search#Vertex_orderings">reverse
- * post-order</a>. For the above example this results in the numbering 2, 4, 7, 5, 6, 3, 8. Also loop header blocks are
- * detected during the traversal by detecting a repeat visit to a block that is still being processed. This causes the
- * block to be flagged as a loop header and also added to the {@link #loopBlocks} list. The {@code loopBlocks} list
- * contains the blocks at 0, 5, 9, 15, 19, 22, with 22 as the loop header. (N.B. the loop header block is added multiple
- * (4) times to this list). (Should 0 be in? It's not inside the loop).
- *
- * If the {@code computeStoresInLoops} argument to {@code build} is true, the {@code loopBlocks} list is processed to
- * mark all local variables that are stored in the blocks in the list.
+ * Builds a mapping between bytecodes and basic blocks and builds a conservative control flow graph (CFG).
+ * It makes one linear passes over the bytecodes to build the CFG where it detects block headers and connects them.
+ * <br>
+ * It also creates exception dispatch blocks for exception handling. These blocks are between a bytecode that might
+ * throw an exception, and the actual exception handler entries, and are later used to create the type checks with the
+ * exception handler catch types. If a bytecode is covered by an exception handler, this bytecode ends the basic block.
+ * This guarantees that a) control flow cannot be transferred to an exception dispatch block in the middle of a block, and
+ * b) that every block has at most one exception dispatch block (which is always the last entry in the successor list).
+ * <br>
+ * If a bytecode is covered by multiple exception handlers, a chain of exception dispatch blocks is created so that
+ * multiple exception handler types can be checked. The chains are re-used if multiple bytecodes are covered by the same
+ * exception handlers.
+ * <br>
+ * Note that exception unwinds, i.e., bytecodes that can throw an exception but the exception is not handled in this method,
+ * do not end a basic block. Not modeling the exception unwind block reduces the complexity of the CFG, and there is no
+ * algorithm yet where the exception unwind block would matter.
+ * <br>
+ * The class also handles subroutines (jsr and ret bytecodes): subroutines are inlined by duplicating the subroutine blocks.
+ * This is limited to simple, structured subroutines with a maximum subroutine nesting of 4. Otherwise, a bailout is thrown.
+ * <br>
+ * Loops in the methods are detected. If a method contains an irreducible loop (a loop with more than one entry), a bailout is
+ * thrown. This simplifies the compiler later on since only structured loops need to be supported.
+ * <br>
+ * A data flow analysis computes the live local variables from the point of view of the interpreter. The result is used later
+ * to prune frame states, i.e., remove local variable entries that are guaranteed to be never used again (even in the case of
+ * deoptimization).
+ * <br>
+ * The algorithms and analysis in this class are conservative and do not use any assumptions or profiling information.
  */
 public final class BciBlockMapping {
 
@@ -129,7 +79,6 @@
 
         public ArrayList<Block> successors = new ArrayList<>(2);
         public long exits;
-        public int normalSuccessors;
 
         private boolean visited;
         private boolean active;
@@ -147,6 +96,20 @@
         private BitMap localsLiveGen;
         private BitMap localsLiveKill;
 
+        public Block exceptionDispatchBlock() {
+            if (successors.size() > 0 && successors.get(successors.size() - 1) instanceof ExceptionDispatchBlock) {
+                return successors.get(successors.size() - 1);
+            }
+            return null;
+        }
+
+        public int numNormalSuccessors() {
+            if  (exceptionDispatchBlock() != null) {
+                return successors.size() - 1;
+            }
+            return successors.size();
+        }
+
         public Block copy() {
             try {
                 Block block = (Block) super.clone();
@@ -175,7 +138,9 @@
         }
     }
 
-    public static class ExceptionBlock extends Block {
+    public static class ExceptionDispatchBlock extends Block {
+        private HashMap<RiExceptionHandler, ExceptionDispatchBlock> exceptionDispatch = new HashMap<>();
+
         public RiExceptionHandler handler;
         public int deoptBci;
     }
@@ -185,11 +150,9 @@
      */
     public final List<Block> blocks;
     public final RiResolvedMethod method;
-    public final BitSet canTrap;
     public boolean hasJsrBytecodes;
     public Block startBlock;
 
-    private final OptimisticOptimizations optimisticOpts;
     private final BytecodeStream stream;
     private final RiExceptionHandler[] exceptionHandlers;
     private Block[] blockMap;
@@ -199,28 +162,21 @@
      * Creates a new BlockMap instance from bytecode of the given method .
      * @param method the compiler interface method containing the code
      */
-    public BciBlockMapping(RiResolvedMethod method, OptimisticOptimizations optimisticOpts) {
+    public BciBlockMapping(RiResolvedMethod method) {
         this.method = method;
-        this.optimisticOpts = optimisticOpts;
         exceptionHandlers = method.exceptionHandlers();
         stream = new BytecodeStream(method.code());
         this.blockMap = new Block[method.codeSize()];
-        this.canTrap = new BitSet(blockMap.length);
         this.blocks = new ArrayList<>();
         this.loopHeaders = new Block[64];
     }
 
-    public RiExceptionHandler[] exceptionHandlers() {
-        return exceptionHandlers;
-    }
-
     /**
      * Builds the block map and conservative CFG and numbers blocks.
      */
     public void build() {
         makeExceptionEntries();
         iterateOverBytecodes();
-        addExceptionEdges();
         if (hasJsrBytecodes) {
             if (!GraalOptions.SupportJsrBytecodes) {
                 throw new JsrNotSupportedBailout("jsr/ret parsing disabled");
@@ -237,6 +193,8 @@
 
         startBlock = blockMap[0];
 
+        assert verify();
+
         // Discard big arrays so that they can be GCed
         blockMap = null;
         if (Debug.isLogEnabled()) {
@@ -252,6 +210,21 @@
         }
     }
 
+    private boolean verify() {
+        for (Block block : blocks) {
+            assert blocks.get(block.blockID) == block;
+
+            for (int i = 0; i < block.successors.size(); i++) {
+                Block sux = block.successors.get(i);
+                if (sux instanceof ExceptionDispatchBlock) {
+                    assert i == block.successors.size() - 1 : "Only one exception handler allowed, and it must be last in successors list";
+                }
+            }
+        }
+
+        return true;
+    }
+
     private void initializeBlockIds() {
         for (int i = 0; i < blocks.size(); i++) {
             blocks.get(i).blockID = i;
@@ -270,7 +243,6 @@
         // iterate over the bytecodes top to bottom.
         // mark the entrypoints of basic blocks and build lists of successors for
         // all bytecodes that end basic blocks (i.e. goto, ifs, switches, throw, jsr, returns, ret)
-        RiProfilingInfo profilingInfo = method.profilingInfo();
         Block current = null;
         stream.setBCI(0);
         while (stream.currentBC() != Bytecodes.END) {
@@ -279,7 +251,7 @@
             if (current == null || blockMap[bci] != null) {
                 Block b = makeBlock(bci);
                 if (current != null) {
-                    setSuccessors(current.endBci, b);
+                    addSuccessor(current.endBci, b);
                 }
                 current = b;
             }
@@ -298,7 +270,10 @@
                 }
                 case ATHROW: {
                     current = null;
-                    canTrap.set(bci);
+                    ExceptionDispatchBlock handler = handleExceptions(bci);
+                    if (handler != null) {
+                        addSuccessor(bci, handler);
+                    }
                     break;
                 }
                 case IFEQ:      // fall through
@@ -318,25 +293,24 @@
                 case IFNULL:    // fall through
                 case IFNONNULL: {
                     current = null;
-                    setSuccessors(bci, makeBlock(stream.readBranchDest()), makeBlock(stream.nextBCI()));
+                    addSuccessor(bci, makeBlock(stream.readBranchDest()));
+                    addSuccessor(bci, makeBlock(stream.nextBCI()));
                     break;
                 }
                 case GOTO:
                 case GOTO_W: {
                     current = null;
-                    setSuccessors(bci, makeBlock(stream.readBranchDest()));
+                    addSuccessor(bci, makeBlock(stream.readBranchDest()));
                     break;
                 }
                 case TABLESWITCH: {
                     current = null;
-                    BytecodeTableSwitch sw = new BytecodeTableSwitch(stream, bci);
-                    setSuccessors(bci, makeSwitchSuccessors(sw));
+                    addSwitchSuccessors(bci, new BytecodeTableSwitch(stream, bci));
                     break;
                 }
                 case LOOKUPSWITCH: {
                     current = null;
-                    BytecodeLookupSwitch sw = new BytecodeLookupSwitch(stream, bci);
-                    setSuccessors(bci, makeSwitchSuccessors(sw));
+                    addSwitchSuccessors(bci, new BytecodeLookupSwitch(stream, bci));
                     break;
                 }
                 case JSR:
@@ -350,7 +324,7 @@
                     current.jsrSuccessor = b1;
                     current.jsrReturnBci = stream.nextBCI();
                     current = null;
-                    setSuccessors(bci, b1);
+                    addSuccessor(bci, b1);
                     break;
                 }
                 case RET: {
@@ -363,8 +337,11 @@
                 case INVOKESTATIC:
                 case INVOKEVIRTUAL: {
                     current = null;
-                    setSuccessors(bci, makeBlock(stream.nextBCI()));
-                    canTrap.set(bci);
+                    addSuccessor(bci, makeBlock(stream.nextBCI()));
+                    ExceptionDispatchBlock handler = handleExceptions(bci);
+                    if (handler != null) {
+                        addSuccessor(bci, handler);
+                    }
                     break;
                 }
                 case IASTORE:
@@ -385,10 +362,11 @@
                 case SALOAD:
                 case PUTFIELD:
                 case GETFIELD: {
-                    if (GraalOptions.AllowExplicitExceptionChecks) {
-                        if (!optimisticOpts.useExceptionProbability() || profilingInfo.getExceptionSeen(bci) != RiExceptionSeen.FALSE) {
-                            canTrap.set(bci);
-                        }
+                    ExceptionDispatchBlock handler = handleExceptions(bci);
+                    if (handler != null) {
+                        current = null;
+                        addSuccessor(bci, makeBlock(stream.nextBCI()));
+                        addSuccessor(bci, handler);
                     }
                 }
             }
@@ -411,12 +389,10 @@
             newBlock.startBci = startBci;
             newBlock.endBci = oldBlock.endBci;
             newBlock.successors.addAll(oldBlock.successors);
-            newBlock.normalSuccessors = oldBlock.normalSuccessors;
 
             oldBlock.endBci = startBci - 1;
             oldBlock.successors.clear();
             oldBlock.successors.add(newBlock);
-            oldBlock.normalSuccessors = 1;
 
             for (int i = startBci; i <= newBlock.endBci; i++) {
                 blockMap[i] = newBlock;
@@ -428,26 +404,19 @@
         }
     }
 
-    private Block[] makeSwitchSuccessors(BytecodeSwitch tswitch) {
-        int max = tswitch.numberOfCases();
-        Block[] successors = new Block[max + 1];
-        for (int i = 0; i < max; i++) {
-            successors[i] = makeBlock(tswitch.targetAt(i));
+    private void addSwitchSuccessors(int predBci, BytecodeSwitch bswitch) {
+        for (int i = 0; i < bswitch.numberOfCases(); i++) {
+            addSuccessor(predBci, makeBlock(bswitch.targetAt(i)));
         }
-        successors[max] = makeBlock(tswitch.defaultTarget());
-        return successors;
+        addSuccessor(predBci, makeBlock(bswitch.defaultTarget()));
     }
 
-    private void setSuccessors(int predBci, Block... successors) {
+    private void addSuccessor(int predBci, Block sux) {
         Block predecessor = blockMap[predBci];
-        assert predecessor.successors.size() == 0;
-        for (Block sux : successors) {
-            if (sux.isExceptionEntry) {
-                throw new CiBailout("Exception handler can be reached by both normal and exceptional control flow");
-            }
-            predecessor.successors.add(sux);
+        if (sux.isExceptionEntry) {
+            throw new CiBailout("Exception handler can be reached by both normal and exceptional control flow");
         }
-        predecessor.normalSuccessors = successors.length;
+        predecessor.successors.add(sux);
     }
 
     private final HashSet<Block> jsrVisited = new HashSet<>();
@@ -505,50 +474,38 @@
         }
     }
 
-    private HashMap<RiExceptionHandler, ExceptionBlock> exceptionDispatch = new HashMap<>();
+
+    private HashMap<RiExceptionHandler, ExceptionDispatchBlock> initialExceptionDispatch = new HashMap<>();
+
+    private ExceptionDispatchBlock handleExceptions(int bci) {
+        ExceptionDispatchBlock lastHandler = null;
+
+        for (int i = exceptionHandlers.length - 1; i >= 0; i--) {
+            RiExceptionHandler h = exceptionHandlers[i];
+            if (h.startBCI() <= bci && bci < h.endBCI()) {
+                if (h.isCatchAll()) {
+                    // Discard all information about succeeding exception handlers, since they can never be reached.
+                    lastHandler = null;
+                }
 
-    private Block makeExceptionDispatch(List<RiExceptionHandler> handlers, int index, int bci) {
-        RiExceptionHandler handler = handlers.get(index);
-        if (handler.isCatchAll()) {
-            return blockMap[handler.handlerBCI()];
-        }
-        ExceptionBlock block = exceptionDispatch.get(handler);
-        if (block == null) {
-            block = new ExceptionBlock();
-            block.startBci = -1;
-            block.endBci = -1;
-            block.deoptBci = bci;
-            block.handler = handler;
-            block.successors.add(blockMap[handler.handlerBCI()]);
-            if (index < handlers.size() - 1) {
-                block.successors.add(makeExceptionDispatch(handlers, index + 1, bci));
-            }
-            exceptionDispatch.put(handler, block);
-        }
-        return block;
-    }
-
-    private void addExceptionEdges() {
-        for (int bci = canTrap.nextSetBit(0); bci >= 0; bci = canTrap.nextSetBit(bci + 1)) {
-            Block block = blockMap[bci];
-
-            ArrayList<RiExceptionHandler> handlers = null;
-            for (RiExceptionHandler h : this.exceptionHandlers) {
-                if (h.startBCI() <= bci && bci < h.endBCI()) {
-                    if (handlers == null) {
-                        handlers = new ArrayList<>();
+                HashMap<RiExceptionHandler, ExceptionDispatchBlock> exceptionDispatch = lastHandler != null ? lastHandler.exceptionDispatch : initialExceptionDispatch;
+                ExceptionDispatchBlock curHandler = exceptionDispatch.get(h);
+                if (curHandler == null) {
+                    curHandler = new ExceptionDispatchBlock();
+                    curHandler.startBci = -1;
+                    curHandler.endBci = -1;
+                    curHandler.deoptBci = bci;
+                    curHandler.handler = h;
+                    curHandler.successors.add(blockMap[h.handlerBCI()]);
+                    if (lastHandler != null) {
+                        curHandler.successors.add(lastHandler);
                     }
-                    handlers.add(h);
-                    if (h.isCatchAll()) {
-                        break;
-                    }
+                    exceptionDispatch.put(h, curHandler);
                 }
-            }
-            if (handlers != null) {
-                Block dispatch = makeExceptionDispatch(handlers, 0, bci);
-                block.successors.add(dispatch);
+                lastHandler = curHandler;
             }
         }
+        return lastHandler;
     }
 
     private boolean loopChanges;
@@ -774,15 +731,6 @@
                     block.localsLiveIn.setFrom(block.localsLiveOut);
                     block.localsLiveIn.setDifference(block.localsLiveKill);
                     block.localsLiveIn.setUnion(block.localsLiveGen);
-
-                    for (Block sux : block.successors) {
-                        if (sux instanceof ExceptionBlock) {
-                            // Exception handler blocks can be reached from anywhere within the block jumping to them,
-                            // so we conservatively assume local variables require by the exception handler are live both
-                            // at the beginning and end of the block.
-                            blockChanged = block.localsLiveIn.setUnionWithResult(sux.localsLiveIn) || blockChanged;
-                        }
-                    }
                     Debug.log("  end   B%d  [%d, %d]  in: %s  out: %s  gen: %s  kill: %s", block.blockID, block.startBci, block.endBci, block.localsLiveIn, block.localsLiveOut, block.localsLiveGen, block.localsLiveKill);
                 }
                 changed |= blockChanged;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri Apr 20 14:13:59 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri Apr 20 14:14:42 2012 +0200
@@ -121,22 +121,10 @@
         return graph.add(new FrameState(method, bci, locals, stack, stackSize, rethrowException, false));
     }
 
-    public FrameState duplicateWithoutStack(int bci) {
-        return graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, false, false));
-    }
-
-
     public FrameStateBuilder copy() {
         return new FrameStateBuilder(method, graph, Arrays.copyOf(locals, locals.length), Arrays.copyOf(stack, stack.length), stackSize, rethrowException);
     }
 
-    public FrameStateBuilder copyWithException(ValueNode exceptionObject) {
-        ValueNode[] newStack = new ValueNode[stack.length];
-        newStack[0] = exceptionObject;
-        return new FrameStateBuilder(method, graph, Arrays.copyOf(locals, locals.length), newStack, 1, true);
-    }
-
-
     public boolean isCompatibleWith(FrameStateBuilder other) {
         assert method == other.method && graph == other.graph && localsSize() == other.localsSize() : "Can only compare frame states of the same method";
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Apr 20 14:13:59 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Apr 20 14:14:42 2012 +0200
@@ -34,10 +34,9 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.java.BciBlockMapping.Block;
-import com.oracle.graal.java.BciBlockMapping.ExceptionBlock;
+import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
 import com.oracle.graal.java.bytecode.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
@@ -70,7 +69,6 @@
 
     private final RiRuntime runtime;
     private RiConstantPool constantPool;
-    private RiExceptionHandler[] exceptionHandlers;
     private RiResolvedMethod method;
     private RiProfilingInfo profilingInfo;
 
@@ -81,13 +79,11 @@
     private Block currentBlock;
 
     private ValueNode methodSynchronizedObject;
-    private ExceptionBlock unwindBlock;
+    private ExceptionDispatchBlock unwindBlock;
     private Block returnBlock;
 
     private FixedWithNextNode lastInstr;                 // the last instruction added
 
-    private BitSet canTrapBitSet;
-
     private final GraphBuilderConfiguration graphBuilderConfig;
     private final OptimisticOptimizations optimisticOpts;
 
@@ -125,7 +121,6 @@
         unwindBlock = null;
         returnBlock = null;
         methodSynchronizedObject = null;
-        exceptionHandlers = null;
         this.currentGraph = graph;
         this.frameState = new FrameStateBuilder(method, graph, graphBuilderConfig.eagerResolving());
         build();
@@ -137,7 +132,7 @@
     }
 
     private BciBlockMapping createBlockMap() {
-        BciBlockMapping map = new BciBlockMapping(method, optimisticOpts);
+        BciBlockMapping map = new BciBlockMapping(method);
         map.build();
         Debug.dump(map, CiUtil.format("After block building %f %R %H.%n(%P)", method));
 
@@ -156,9 +151,6 @@
 
         // compute the block map, setup exception handlers and get the entrypoint(s)
         BciBlockMapping blockMap = createBlockMap();
-        this.canTrapBitSet = blockMap.canTrap;
-
-        exceptionHandlers = blockMap.exceptionHandlers();
         loopHeaders = blockMap.loopHeaders;
 
         lastInstr = currentGraph.start();
@@ -212,7 +204,7 @@
 
     private Block unwindBlock(int bci) {
         if (unwindBlock == null) {
-            unwindBlock = new ExceptionBlock();
+            unwindBlock = new ExceptionDispatchBlock();
             unwindBlock.startBci = -1;
             unwindBlock.endBci = -1;
             unwindBlock.deoptBci = bci;
@@ -257,69 +249,38 @@
 
     private BeginNode handleException(ValueNode exceptionObject, int bci) {
         assert bci == FrameState.BEFORE_BCI || bci == bci() : "invalid bci";
-
-        if (optimisticOpts.useExceptionProbability()) {
-            // be conservative if information was not recorded (could result in endless recompiles otherwise)
-            if (bci != FrameState.BEFORE_BCI && exceptionObject == null && profilingInfo.getExceptionSeen(bci) == RiExceptionSeen.FALSE) {
-                return null;
-            } else {
-                Debug.log("Creating exception edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, profilingInfo.getExceptionSeen(bci));
-            }
-        }
+        Debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, profilingInfo.getExceptionSeen(bci));
 
-        RiExceptionHandler firstHandler = null;
-        // join with all potential exception handlers
-        if (exceptionHandlers != null) {
-            for (RiExceptionHandler handler : exceptionHandlers) {
-                if (covers(handler, bci)) {
-                    firstHandler = handler;
-                    break;
-                }
-            }
+        Block 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 = unwindBlock(bci);
         }
 
-        Block dispatchBlock = null;
-        if (firstHandler == null) {
-            dispatchBlock = unwindBlock(bci);
-        } else {
-            for (int i = currentBlock.normalSuccessors; i < currentBlock.successors.size(); i++) {
-                Block block = currentBlock.successors.get(i);
-                if (block instanceof ExceptionBlock && ((ExceptionBlock) block).handler == firstHandler) {
-                    dispatchBlock = block;
-                    break;
-                }
-                if (isCatchAll(firstHandler) && block.startBci == firstHandler.handlerBCI()) {
-                    dispatchBlock = block;
-                    break;
-                }
-            }
-        }
+        FrameStateBuilder dispatchState = frameState.copy();
+        dispatchState.clearStack();
+
+        BeginNode dispatchBegin = currentGraph.add(new BeginNode());
+        dispatchBegin.setStateAfter(dispatchState.create(bci));
 
-        // TODO (thomaswue): Merge BeginNode with ExceptionObject node to get a correct and uniform FrameState.
-        BeginNode p = currentGraph.add(new BeginNode());
-        p.setStateAfter(frameState.duplicateWithoutStack(bci));
+        if (exceptionObject == null) {
+            ExceptionObjectNode newExceptionObject = currentGraph.add(new ExceptionObjectNode());
+            dispatchState.apush(newExceptionObject);
+            dispatchState.setRethrowException(true);
+            newExceptionObject.setStateAfter(dispatchState.create(bci));
 
-        ValueNode currentExceptionObject;
-        ExceptionObjectNode newObj = null;
-        if (exceptionObject == null) {
-            newObj = currentGraph.add(new ExceptionObjectNode());
-            currentExceptionObject = newObj;
+            FixedNode target = createTarget(dispatchBlock, dispatchState);
+            dispatchBegin.setNext(newExceptionObject);
+            newExceptionObject.setNext(target);
         } else {
-            currentExceptionObject = exceptionObject;
+            dispatchState.apush(exceptionObject);
+            dispatchState.setRethrowException(true);
+
+            FixedNode target = createTarget(dispatchBlock, dispatchState);
+            dispatchBegin.setNext(target);
         }
-        FrameStateBuilder stateWithException = frameState.copyWithException(currentExceptionObject);
-        if (newObj != null) {
-            newObj.setStateAfter(stateWithException.create(bci));
-        }
-        FixedNode target = createTarget(dispatchBlock, stateWithException);
-        if (exceptionObject == null) {
-            ExceptionObjectNode eObj = (ExceptionObjectNode) currentExceptionObject;
-            eObj.setNext(target);
-            p.setNext(eObj);
-        } else {
-            p.setNext(target);
-        }
-        return p;
+        return dispatchBegin;
     }
 
     private void genLoadConstant(int cpi, int opcode) {
@@ -543,12 +504,12 @@
             probability = 1;
         }
         appendGoto(createTarget(probability, currentBlock.successors.get(0), frameState));
-        assert currentBlock.normalSuccessors == 1;
+        assert currentBlock.numNormalSuccessors() == 1;
     }
 
     private void ifNode(ValueNode x, Condition cond, ValueNode y) {
         assert !x.isDeleted() && !y.isDeleted();
-        assert currentBlock.normalSuccessors == 2 : currentBlock.normalSuccessors;
+        assert currentBlock.numNormalSuccessors() == 2;
         Block trueBlock = currentBlock.successors.get(0);
         Block falseBlock = currentBlock.successors.get(1);
         if (trueBlock == falseBlock) {
@@ -588,11 +549,11 @@
         ifNode(x, cond, y);
     }
 
-    private void genThrow(int bci) {
+    private void genThrow() {
         ValueNode exception = frameState.apop();
         FixedGuardNode node = currentGraph.add(new FixedGuardNode(currentGraph.unique(new NullCheckNode(exception, false)), RiDeoptReason.NullCheckException, RiDeoptAction.InvalidateReprofile, graphId));
         append(node);
-        append(handleException(exception, bci));
+        append(handleException(exception, bci()));
     }
 
     private RiType lookupType(int cpi, int bytecode) {
@@ -802,7 +763,7 @@
         }
     }
 
-    private ExceptionInfo emitNullCheck(ValueNode receiver) {
+    private void emitNullCheck(ValueNode receiver) {
         BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode());
         BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode());
         IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new NullCheckNode(receiver, false)), trueSucc, falseSucc, 1));
@@ -812,16 +773,16 @@
 
         if (GraalOptions.OmitHotExceptionStacktrace) {
             ValueNode exception = ConstantNode.forObject(new NullPointerException(), runtime, currentGraph);
-            return new ExceptionInfo(falseSucc, exception);
+            falseSucc.setNext(handleException(exception, bci()));
         } else {
             RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateNullPointerException));
             call.setStateAfter(frameState.create(bci()));
             falseSucc.setNext(call);
-            return new ExceptionInfo(call, call);
+            call.setNext(handleException(call, bci()));
         }
     }
 
-    private ExceptionInfo emitBoundsCheck(ValueNode index, ValueNode length) {
+    private void emitBoundsCheck(ValueNode index, ValueNode length) {
         BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode());
         BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode());
         IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(index, Condition.BT, length)), trueSucc, falseSucc, 1));
@@ -831,51 +792,27 @@
 
         if (GraalOptions.OmitHotExceptionStacktrace) {
             ValueNode exception = ConstantNode.forObject(new ArrayIndexOutOfBoundsException(), runtime, currentGraph);
-            return new ExceptionInfo(falseSucc, exception);
+            falseSucc.setNext(handleException(exception, bci()));
         } else {
             RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateOutOfBoundsException, new ValueNode[] {index}));
             call.setStateAfter(frameState.create(bci()));
             falseSucc.setNext(call);
-            return new ExceptionInfo(call, call);
+            call.setNext(handleException(call, bci()));
         }
     }
 
     private void emitExplicitExceptions(ValueNode receiver, ValueNode outOfBoundsIndex) {
         assert receiver != null;
+        if (!GraalOptions.AllowExplicitExceptionChecks || (optimisticOpts.useExceptionProbability() && profilingInfo.getExceptionSeen(bci()) == RiExceptionSeen.FALSE)) {
+            return;
+        }
 
-        if (canTrapBitSet.get(bci()) && GraalOptions.AllowExplicitExceptionChecks) {
-            ArrayList<ExceptionInfo> exceptions = new ArrayList<>(2);
-            exceptions.add(emitNullCheck(receiver));
-            if (outOfBoundsIndex != null) {
-                ArrayLengthNode length = currentGraph.add(new ArrayLengthNode(receiver));
-                append(length);
-                exceptions.add(emitBoundsCheck(outOfBoundsIndex, length));
-            }
-            final ExceptionInfo exception;
-            if (exceptions.size() == 1) {
-                exception = exceptions.get(0);
-            } else {
-                assert exceptions.size() > 1;
-                MergeNode merge = currentGraph.add(new MergeNode());
-                PhiNode phi = currentGraph.unique(new PhiNode(CiKind.Object, merge, PhiType.Value));
-                for (ExceptionInfo info : exceptions) {
-                    EndNode end = currentGraph.add(new EndNode());
-                    info.exceptionEdge.setNext(end);
-                    merge.addForwardEnd(end);
-                    phi.addInput(info.exception);
-                }
-                merge.setStateAfter(frameState.create(bci()));
-                exception = new ExceptionInfo(merge, phi);
-            }
-
-            FixedNode entry = handleException(exception.exception, bci());
-            if (entry != null) {
-                exception.exceptionEdge.setNext(entry);
-            } else {
-                exception.exceptionEdge.setNext(createTarget(unwindBlock(bci()), frameState.copyWithException(exception.exception)));
-            }
-            Debug.metric("ExplicitExceptions").increment();
+        emitNullCheck(receiver);
+        if (outOfBoundsIndex != null) {
+            ValueNode length = append(currentGraph.add(new ArrayLengthNode(receiver)));
+            emitBoundsCheck(outOfBoundsIndex, length);
         }
+        Debug.metric("ExplicitExceptions").increment();
     }
 
     private void genPutField(RiField field) {
@@ -1031,25 +968,27 @@
             deoptimize.setMessage("invoke " + targetMethod.name());
             append(deoptimize);
             frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, currentGraph));
-        } else {
-            MethodCallTargetNode callTarget = currentGraph.add(new MethodCallTargetNode(invokeKind, targetMethod, args, targetMethod.signature().returnType(method.holder())));
-            BeginNode exceptionEdge = handleException(null, bci());
-            ValueNode result;
-            if (exceptionEdge != null) {
-                InvokeWithExceptionNode invoke = currentGraph.add(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci(), graphId));
-                result = append(invoke);
-                frameState.pushReturn(resultType, result);
-                Block nextBlock = currentBlock.successors.get(0);
+            return;
+        }
+
+        MethodCallTargetNode callTarget = currentGraph.add(new MethodCallTargetNode(invokeKind, targetMethod, args, targetMethod.signature().returnType(method.holder())));
+        // be conservative if information was not recorded (could result in endless recompiles otherwise)
+        if (optimisticOpts.useExceptionProbability() && profilingInfo.getExceptionSeen(bci()) == RiExceptionSeen.FALSE) {
+            ValueNode result = appendWithBCI(currentGraph.add(new InvokeNode(callTarget, bci(), graphId)));
+            frameState.pushReturn(resultType, result);
 
-                assert bci() == currentBlock.endBci;
-                frameState.clearNonLiveLocals(currentBlock.localsLiveOut);
+        } else {
+            BeginNode exceptionEdge = handleException(null, bci());
+            InvokeWithExceptionNode invoke = currentGraph.add(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci(), graphId));
+            ValueNode result = append(invoke);
+            frameState.pushReturn(resultType, result);
+            Block nextBlock = currentBlock.successors.get(0);
 
-                invoke.setNext(createTarget(nextBlock, frameState));
-                invoke.setStateAfter(frameState.create(nextBlock.startBci));
-            } else {
-                result = appendWithBCI(currentGraph.add(new InvokeNode(callTarget, bci(), graphId)));
-                frameState.pushReturn(resultType, result);
-            }
+            assert bci() == currentBlock.endBci;
+            frameState.clearNonLiveLocals(currentBlock.localsLiveOut);
+
+            invoke.setNext(createTarget(nextBlock, frameState));
+            invoke.setStateAfter(frameState.create(nextBlock.startBci));
         }
     }
 
@@ -1129,7 +1068,7 @@
         BytecodeTableSwitch ts = new BytecodeTableSwitch(stream(), bci);
 
         int nofCases = ts.numberOfCases() + 1; // including default case
-        assert currentBlock.normalSuccessors == nofCases;
+        assert currentBlock.numNormalSuccessors() == nofCases;
 
         double[] probabilities = switchProbability(nofCases, bci);
         TableSwitchNode tableSwitch = currentGraph.add(new TableSwitchNode(value, ts.lowKey(), probabilities));
@@ -1169,7 +1108,7 @@
         BytecodeLookupSwitch ls = new BytecodeLookupSwitch(stream(), bci);
 
         int nofCases = ls.numberOfCases() + 1; // including default case
-        assert currentBlock.normalSuccessors == nofCases;
+        assert currentBlock.numNormalSuccessors() == nofCases;
 
         int[] keys = new int[nofCases - 1];
         for (int i = 0; i < nofCases - 1; ++i) {
@@ -1244,8 +1183,8 @@
                 });
 
                 int bci = targetBlock.startBci;
-                if (targetBlock instanceof ExceptionBlock) {
-                    bci = ((ExceptionBlock) targetBlock).deoptBci;
+                if (targetBlock instanceof ExceptionDispatchBlock) {
+                    bci = ((ExceptionDispatchBlock) targetBlock).deoptBci;
                 }
                 FrameStateBuilder newState = state.copy();
                 for (Block loop : exitLoops) {
@@ -1382,8 +1321,8 @@
         frameState.cleanupDeletedPhis();
         if (lastInstr instanceof MergeNode) {
             int bci = block.startBci;
-            if (block instanceof ExceptionBlock) {
-                bci = ((ExceptionBlock) block).deoptBci;
+            if (block instanceof ExceptionDispatchBlock) {
+                bci = ((ExceptionDispatchBlock) block).deoptBci;
             }
             ((MergeNode) lastInstr).setStateAfter(frameState.create(bci));
         }
@@ -1394,8 +1333,8 @@
         } else if (block == unwindBlock) {
             frameState.setRethrowException(false);
             createUnwind();
-        } else if (block instanceof ExceptionBlock) {
-            createExceptionDispatch((ExceptionBlock) block);
+        } else if (block instanceof ExceptionDispatchBlock) {
+            createExceptionDispatch((ExceptionDispatchBlock) block);
         } else {
             frameState.setRethrowException(false);
             iterateBytecodesForBlock(block);
@@ -1422,6 +1361,7 @@
     }
 
     private void createUnwind() {
+        assert frameState.stackSize() == 1 : frameState;
         synchronizedEpilogue(FrameState.AFTER_EXCEPTION_BCI);
         UnwindNode unwindNode = currentGraph.add(new UnwindNode(frameState.apop()));
         append(unwindNode);
@@ -1454,37 +1394,37 @@
         }
     }
 
-    private void createExceptionDispatch(ExceptionBlock block) {
-        if (block.handler == null) {
-            assert frameState.stackSize() == 1 : "only exception object expected on stack, actual size: " + frameState.stackSize();
-            createUnwind();
-        } else {
-            assert frameState.stackSize() == 1 : frameState;
+    private void createExceptionDispatch(ExceptionDispatchBlock block) {
+        assert frameState.stackSize() == 1 : frameState;
+        if (block.handler.isCatchAll()) {
+            assert block.successors.size() == 1;
+            appendGoto(createTarget(block.successors.get(0), frameState));
+            return;
+        }
 
-            RiType catchType = block.handler.catchType();
-            if (graphBuilderConfig.eagerResolving()) {
-                catchType = lookupType(block.handler.catchTypeCPI(), INSTANCEOF);
-            }
-            boolean initialized = (catchType instanceof RiResolvedType);
-            if (initialized && graphBuilderConfig.getSkippedExceptionTypes() != null) {
-                RiResolvedType resolvedCatchType = (RiResolvedType) catchType;
-                for (RiResolvedType skippedType : graphBuilderConfig.getSkippedExceptionTypes()) {
-                    initialized &= !resolvedCatchType.isSubtypeOf(skippedType);
-                    if (!initialized) {
-                        break;
-                    }
+        RiType catchType = block.handler.catchType();
+        if (graphBuilderConfig.eagerResolving()) {
+            catchType = lookupType(block.handler.catchTypeCPI(), INSTANCEOF);
+        }
+        boolean initialized = (catchType instanceof RiResolvedType);
+        if (initialized && graphBuilderConfig.getSkippedExceptionTypes() != null) {
+            RiResolvedType resolvedCatchType = (RiResolvedType) catchType;
+            for (RiResolvedType skippedType : graphBuilderConfig.getSkippedExceptionTypes()) {
+                initialized &= !resolvedCatchType.isSubtypeOf(skippedType);
+                if (!initialized) {
+                    break;
                 }
             }
+        }
 
-            ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, catchType, initialized);
-            if (typeInstruction != null) {
-                Block nextBlock = block.successors.size() == 1 ? unwindBlock(block.deoptBci) : block.successors.get(1);
-                FixedNode catchSuccessor = createTarget(block.successors.get(0), frameState);
-                FixedNode nextDispatch = createTarget(nextBlock, frameState);
-                ValueNode exception = frameState.stackAt(0);
-                IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new InstanceOfNode(typeInstruction, (RiResolvedType) catchType, exception, false)), catchSuccessor, nextDispatch, 0.5));
-                append(ifNode);
-            }
+        ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, catchType, initialized);
+        if (typeInstruction != null) {
+            Block nextBlock = block.successors.size() == 1 ? unwindBlock(block.deoptBci) : block.successors.get(1);
+            FixedNode catchSuccessor = createTarget(block.successors.get(0), frameState);
+            FixedNode nextDispatch = createTarget(nextBlock, frameState);
+            ValueNode exception = frameState.stackAt(0);
+            IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new InstanceOfNode(typeInstruction, (RiResolvedType) catchType, exception, false)), catchSuccessor, nextDispatch, 0.5));
+            append(ifNode);
         }
     }
 
@@ -1554,6 +1494,10 @@
 
             stream.next();
             bci = stream.currentBCI();
+
+            if (bci > block.endBci) {
+                frameState.clearNonLiveLocals(currentBlock.localsLiveOut);
+            }
             if (lastInstr instanceof StateSplit) {
                 StateSplit stateSplit = (StateSplit) lastInstr;
                 if (stateSplit.stateAfter() == null && stateSplit.needsStateAfter()) {
@@ -1563,7 +1507,7 @@
             if (bci < endBCI) {
                 if (bci > block.endBci) {
                     assert !block.successors.get(0).isExceptionEntry;
-                    assert block.normalSuccessors == 1;
+                    assert block.numNormalSuccessors() == 1;
                     // we fell through to the next block, add a goto and break
                     appendGoto(createTarget(block.successors.get(0), frameState));
                     break;
@@ -1781,7 +1725,7 @@
             case NEWARRAY       : genNewTypeArray(stream.readLocalIndex()); break;
             case ANEWARRAY      : genNewObjectArray(stream.readCPI()); break;
             case ARRAYLENGTH    : genArrayLength(); break;
-            case ATHROW         : genThrow(stream.currentBCI()); break;
+            case ATHROW         : genThrow(); break;
             case CHECKCAST      : genCheckCast(); break;
             case INSTANCEOF     : genInstanceOf(); break;
             case MONITORENTER   : genMonitorEnter(frameState.apop()); break;