# HG changeset patch # User Doug Simon # Date 1334924082 -7200 # Node ID 4e4a868c2b2ae20dcecf5ecd64a8a3900df2b320 # Parent af8958fe5a3a556108f65c0b7a6dc032e2e9b802# Parent 1e153fdac9fb6a9ce353e820a80047f8302c0040 Merge. diff -r af8958fe5a3a -r 4e4a868c2b2a graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java --- 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 not 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) - * - *

More Details on {@link BciBlockMapping#build}

- * - * 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. - * - *

Example

- * - * Consider the following source 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;
- *     }
- * 
- * 
- * - * This is translated by javac to the following bytecode: - * - *
- * 
- *    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
- *    
- * 
- * - * 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}. - *

- * Next the blocks are numbered using reverse - * post-order. 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. + *
+ * 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). + *
+ * 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. + *
+ * 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. + *
+ * 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. + *
+ * 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. + *
+ * 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). + *
+ * 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 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 exceptionDispatch = new HashMap<>(); + public RiExceptionHandler handler; public int deoptBci; } @@ -185,11 +150,9 @@ */ public final List 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 jsrVisited = new HashSet<>(); @@ -505,50 +474,38 @@ } } - private HashMap exceptionDispatch = new HashMap<>(); + + private HashMap 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 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 handlers = null; - for (RiExceptionHandler h : this.exceptionHandlers) { - if (h.startBCI() <= bci && bci < h.endBCI()) { - if (handlers == null) { - handlers = new ArrayList<>(); + HashMap 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; diff -r af8958fe5a3a -r 4e4a868c2b2a graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java --- 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"; diff -r af8958fe5a3a -r 4e4a868c2b2a graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- 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 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;