--- /dev/null 2016-05-31 09:42:47.975716356 -0700
+++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java 2016-12-09 00:50:08.141024527 -0800
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (c) 2009, 2015, 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 org.graalvm.compiler.java;
+
+import static org.graalvm.compiler.bytecode.Bytecodes.AALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.AASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.ARETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.ARRAYLENGTH;
+import static org.graalvm.compiler.bytecode.Bytecodes.ATHROW;
+import static org.graalvm.compiler.bytecode.Bytecodes.BALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.BASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.CALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.CASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.DALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.DASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.DRETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.FALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.FASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.FRETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD;
+import static org.graalvm.compiler.bytecode.Bytecodes.GOTO;
+import static org.graalvm.compiler.bytecode.Bytecodes.GOTO_W;
+import static org.graalvm.compiler.bytecode.Bytecodes.IALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.IASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFEQ;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFGE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFGT;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFLE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFLT;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFNE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFNONNULL;
+import static org.graalvm.compiler.bytecode.Bytecodes.IFNULL;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPEQ;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPNE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPEQ;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGT;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLE;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLT;
+import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPNE;
+import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEDYNAMIC;
+import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE;
+import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL;
+import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC;
+import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL;
+import static org.graalvm.compiler.bytecode.Bytecodes.IRETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.JSR;
+import static org.graalvm.compiler.bytecode.Bytecodes.JSR_W;
+import static org.graalvm.compiler.bytecode.Bytecodes.LALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.LASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.LOOKUPSWITCH;
+import static org.graalvm.compiler.bytecode.Bytecodes.LRETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD;
+import static org.graalvm.compiler.bytecode.Bytecodes.RET;
+import static org.graalvm.compiler.bytecode.Bytecodes.RETURN;
+import static org.graalvm.compiler.bytecode.Bytecodes.SALOAD;
+import static org.graalvm.compiler.bytecode.Bytecodes.SASTORE;
+import static org.graalvm.compiler.bytecode.Bytecodes.TABLESWITCH;
+import static org.graalvm.compiler.core.common.GraalOptions.SupportJsrBytecodes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.graalvm.compiler.bytecode.Bytecode;
+import org.graalvm.compiler.bytecode.BytecodeLookupSwitch;
+import org.graalvm.compiler.bytecode.BytecodeStream;
+import org.graalvm.compiler.bytecode.BytecodeSwitch;
+import org.graalvm.compiler.bytecode.BytecodeTableSwitch;
+import org.graalvm.compiler.bytecode.Bytecodes;
+import org.graalvm.compiler.common.PermanentBailoutException;
+import org.graalvm.compiler.core.common.CollectionsFactory;
+import org.graalvm.compiler.debug.Debug;
+
+import jdk.vm.ci.code.BytecodeFrame;
+import jdk.vm.ci.meta.ExceptionHandler;
+
+/**
+ * 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 {
+
+ public static class BciBlock implements Cloneable {
+
+ protected int id;
+ public int startBci;
+ public int endBci;
+ public boolean isExceptionEntry;
+ public boolean isLoopHeader;
+ public int loopId;
+ public int loopEnd;
+ protected List successors;
+ private int predecessorCount;
+
+ private boolean visited;
+ private boolean active;
+ public long loops;
+ public JSRData jsrData;
+
+ public static class JSRData implements Cloneable {
+ public HashMap jsrAlternatives;
+ public JsrScope jsrScope = JsrScope.EMPTY_SCOPE;
+ public BciBlock jsrSuccessor;
+ public int jsrReturnBci;
+ public BciBlock retSuccessor;
+ public boolean endsWithRet = false;
+
+ public JSRData copy() {
+ try {
+ return (JSRData) this.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+ }
+
+ public BciBlock() {
+ this.successors = new ArrayList<>(4);
+ }
+
+ public BciBlock exceptionDispatchBlock() {
+ if (successors.size() > 0 && successors.get(successors.size() - 1) instanceof ExceptionDispatchBlock) {
+ return successors.get(successors.size() - 1);
+ }
+ return null;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getPredecessorCount() {
+ return this.predecessorCount;
+ }
+
+ public int numNormalSuccessors() {
+ if (exceptionDispatchBlock() != null) {
+ return successors.size() - 1;
+ }
+ return successors.size();
+ }
+
+ public BciBlock copy() {
+ try {
+ BciBlock block = (BciBlock) super.clone();
+ if (block.jsrData != null) {
+ block.jsrData = block.jsrData.copy();
+ }
+ block.successors = new ArrayList<>(successors);
+ return block;
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("B").append(getId());
+ sb.append('[').append(startBci).append("->").append(endBci);
+ if (isLoopHeader || isExceptionEntry) {
+ sb.append(' ');
+ if (isLoopHeader) {
+ sb.append('L');
+ }
+ if (isExceptionEntry) {
+ sb.append('!');
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public int getLoopDepth() {
+ return Long.bitCount(loops);
+ }
+
+ public boolean isLoopHeader() {
+ return isLoopHeader;
+ }
+
+ public boolean isExceptionEntry() {
+ return isExceptionEntry;
+ }
+
+ public BciBlock getSuccessor(int index) {
+ return successors.get(index);
+ }
+
+ /**
+ * Get the loop id of the inner most loop.
+ *
+ * @return the loop id of the most inner loop or -1 if not part of any loop
+ */
+ public int getLoopId() {
+ long l = loops;
+ if (l == 0) {
+ return -1;
+ }
+ int pos = 0;
+ for (int lMask = 1; (l & lMask) == 0; lMask = lMask << 1) {
+ pos++;
+ }
+ return pos;
+ }
+
+ /**
+ * Iterate over loop ids.
+ */
+ public Iterable loopIdIterable() {
+ return new Iterable() {
+ @Override
+ public Iterator iterator() {
+ return idIterator(loops);
+ }
+ };
+ }
+
+ private static Iterator idIterator(long field) {
+ return new Iterator() {
+
+ long l = field;
+ int pos = 0;
+ int lMask = 1;
+
+ @Override
+ public Integer next() {
+ for (; (l & lMask) == 0; lMask = lMask << 1) {
+ pos++;
+ }
+ l &= ~lMask;
+ return pos;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return l != 0;
+ }
+ };
+
+ }
+
+ public double probability() {
+ return 1D;
+ }
+
+ public BciBlock getPostdominator() {
+ return null;
+ }
+
+ private JSRData getOrCreateJSRData() {
+ if (jsrData == null) {
+ jsrData = new JSRData();
+ }
+ return jsrData;
+ }
+
+ void setEndsWithRet() {
+ getOrCreateJSRData().endsWithRet = true;
+ }
+
+ public JsrScope getJsrScope() {
+ if (this.jsrData == null) {
+ return JsrScope.EMPTY_SCOPE;
+ } else {
+ return jsrData.jsrScope;
+ }
+ }
+
+ public boolean endsWithRet() {
+ if (this.jsrData == null) {
+ return false;
+ } else {
+ return jsrData.endsWithRet;
+ }
+ }
+
+ void setRetSuccessor(BciBlock bciBlock) {
+ this.getOrCreateJSRData().retSuccessor = bciBlock;
+ }
+
+ public BciBlock getRetSuccessor() {
+ if (this.jsrData == null) {
+ return null;
+ } else {
+ return jsrData.retSuccessor;
+ }
+ }
+
+ public BciBlock getJsrSuccessor() {
+ if (this.jsrData == null) {
+ return null;
+ } else {
+ return jsrData.jsrSuccessor;
+ }
+ }
+
+ public int getJsrReturnBci() {
+ if (this.jsrData == null) {
+ return -1;
+ } else {
+ return jsrData.jsrReturnBci;
+ }
+ }
+
+ public HashMap getJsrAlternatives() {
+ if (this.jsrData == null) {
+ return null;
+ } else {
+ return jsrData.jsrAlternatives;
+ }
+ }
+
+ public void initJsrAlternatives() {
+ JSRData data = this.getOrCreateJSRData();
+ if (data.jsrAlternatives == null) {
+ data.jsrAlternatives = new HashMap<>();
+ }
+ }
+
+ void setJsrScope(JsrScope nextScope) {
+ this.getOrCreateJSRData().jsrScope = nextScope;
+ }
+
+ void setJsrSuccessor(BciBlock clone) {
+ this.getOrCreateJSRData().jsrSuccessor = clone;
+ }
+
+ void setJsrReturnBci(int bci) {
+ this.getOrCreateJSRData().jsrReturnBci = bci;
+ }
+
+ public int getSuccessorCount() {
+ return successors.size();
+ }
+
+ public List getSuccessors() {
+ return successors;
+ }
+
+ void setId(int i) {
+ this.id = i;
+ }
+
+ public void addSuccessor(BciBlock sux) {
+ successors.add(sux);
+ sux.predecessorCount++;
+ }
+
+ public void clearSucccessors() {
+ for (BciBlock sux : successors) {
+ sux.predecessorCount--;
+ }
+ successors.clear();
+ }
+ }
+
+ public static class ExceptionDispatchBlock extends BciBlock {
+
+ private HashMap exceptionDispatch = new HashMap<>();
+
+ public ExceptionHandler handler;
+ public int deoptBci;
+ }
+
+ /**
+ * The blocks found in this method, in reverse postorder.
+ */
+ private BciBlock[] blocks;
+ public final Bytecode code;
+ public boolean hasJsrBytecodes;
+
+ private final ExceptionHandler[] exceptionHandlers;
+ private BciBlock startBlock;
+ private BciBlock[] loopHeaders;
+
+ private static final int LOOP_HEADER_MAX_CAPACITY = Long.SIZE;
+ private static final int LOOP_HEADER_INITIAL_CAPACITY = 4;
+
+ private int blocksNotYetAssignedId;
+ public int returnCount;
+ private int returnBci;
+
+ /**
+ * Creates a new BlockMap instance from {@code code}.
+ */
+ private BciBlockMapping(Bytecode code) {
+ this.code = code;
+ this.exceptionHandlers = code.getExceptionHandlers();
+ }
+
+ public BciBlock[] getBlocks() {
+ return this.blocks;
+ }
+
+ public int getReturnCount() {
+ return this.returnCount;
+ }
+
+ /**
+ * Builds the block map and conservative CFG and numbers blocks.
+ */
+ public void build(BytecodeStream stream) {
+ int codeSize = code.getCodeSize();
+ BciBlock[] blockMap = new BciBlock[codeSize];
+ makeExceptionEntries(blockMap);
+ iterateOverBytecodes(blockMap, stream);
+ if (hasJsrBytecodes) {
+ if (!SupportJsrBytecodes.getValue()) {
+ throw new JsrNotSupportedBailout("jsr/ret parsing disabled");
+ }
+ createJsrAlternatives(blockMap, blockMap[0]);
+ }
+ if (Debug.isLogEnabled()) {
+ this.log(blockMap, "Before BlockOrder");
+ }
+ computeBlockOrder(blockMap);
+ fixLoopBits(blockMap);
+
+ assert verify();
+
+ startBlock = blockMap[0];
+ if (Debug.isLogEnabled()) {
+ this.log(blockMap, "Before LivenessAnalysis");
+ }
+ }
+
+ private boolean verify() {
+ for (BciBlock block : blocks) {
+ assert blocks[block.getId()] == block;
+
+ for (int i = 0; i < block.getSuccessorCount(); i++) {
+ BciBlock sux = block.getSuccessor(i);
+ if (sux instanceof ExceptionDispatchBlock) {
+ assert i == block.getSuccessorCount() - 1 : "Only one exception handler allowed, and it must be last in successors list";
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void makeExceptionEntries(BciBlock[] blockMap) {
+ // start basic blocks at all exception handler blocks and mark them as exception entries
+ for (ExceptionHandler h : this.exceptionHandlers) {
+ BciBlock xhandler = makeBlock(blockMap, h.getHandlerBCI());
+ xhandler.isExceptionEntry = true;
+ }
+ }
+
+ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) {
+ // 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)
+ BciBlock current = null;
+ stream.setBCI(0);
+ while (stream.currentBC() != Bytecodes.END) {
+ int bci = stream.currentBCI();
+
+ if (current == null || blockMap[bci] != null) {
+ BciBlock b = makeBlock(blockMap, bci);
+ if (current != null) {
+ addSuccessor(blockMap, current.endBci, b);
+ }
+ current = b;
+ }
+ blockMap[bci] = current;
+ current.endBci = bci;
+
+ switch (stream.currentBC()) {
+ case IRETURN: // fall through
+ case LRETURN: // fall through
+ case FRETURN: // fall through
+ case DRETURN: // fall through
+ case ARETURN: // fall through
+ case RETURN: {
+ returnCount++;
+ current = null;
+ returnBci = bci;
+ break;
+ }
+ case ATHROW: {
+ current = null;
+ ExceptionDispatchBlock handler = handleExceptions(blockMap, bci);
+ if (handler != null) {
+ addSuccessor(blockMap, bci, handler);
+ }
+ break;
+ }
+ case IFEQ: // fall through
+ case IFNE: // fall through
+ case IFLT: // fall through
+ case IFGE: // fall through
+ case IFGT: // fall through
+ case IFLE: // fall through
+ case IF_ICMPEQ: // fall through
+ case IF_ICMPNE: // fall through
+ case IF_ICMPLT: // fall through
+ case IF_ICMPGE: // fall through
+ case IF_ICMPGT: // fall through
+ case IF_ICMPLE: // fall through
+ case IF_ACMPEQ: // fall through
+ case IF_ACMPNE: // fall through
+ case IFNULL: // fall through
+ case IFNONNULL: {
+ current = null;
+ addSuccessor(blockMap, bci, makeBlock(blockMap, stream.readBranchDest()));
+ addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI()));
+ break;
+ }
+ case GOTO:
+ case GOTO_W: {
+ current = null;
+ addSuccessor(blockMap, bci, makeBlock(blockMap, stream.readBranchDest()));
+ break;
+ }
+ case TABLESWITCH: {
+ current = null;
+ addSwitchSuccessors(blockMap, bci, new BytecodeTableSwitch(stream, bci));
+ break;
+ }
+ case LOOKUPSWITCH: {
+ current = null;
+ addSwitchSuccessors(blockMap, bci, new BytecodeLookupSwitch(stream, bci));
+ break;
+ }
+ case JSR:
+ case JSR_W: {
+ hasJsrBytecodes = true;
+ int target = stream.readBranchDest();
+ if (target == 0) {
+ throw new JsrNotSupportedBailout("jsr target bci 0 not allowed");
+ }
+ BciBlock b1 = makeBlock(blockMap, target);
+ current.setJsrSuccessor(b1);
+ current.setJsrReturnBci(stream.nextBCI());
+ current = null;
+ addSuccessor(blockMap, bci, b1);
+ break;
+ }
+ case RET: {
+ current.setEndsWithRet();
+ current = null;
+ break;
+ }
+ case INVOKEINTERFACE:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ case INVOKEVIRTUAL:
+ case INVOKEDYNAMIC: {
+ current = null;
+ addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI()));
+ ExceptionDispatchBlock handler = handleExceptions(blockMap, bci);
+ if (handler != null) {
+ addSuccessor(blockMap, bci, handler);
+ }
+ break;
+ }
+ case IASTORE:
+ case LASTORE:
+ case FASTORE:
+ case DASTORE:
+ case AASTORE:
+ case BASTORE:
+ case CASTORE:
+ case SASTORE:
+ case IALOAD:
+ case LALOAD:
+ case FALOAD:
+ case DALOAD:
+ case AALOAD:
+ case BALOAD:
+ case CALOAD:
+ case SALOAD:
+ case ARRAYLENGTH:
+ case PUTFIELD:
+ case GETFIELD: {
+ ExceptionDispatchBlock handler = handleExceptions(blockMap, bci);
+ if (handler != null) {
+ current = null;
+ addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI()));
+ addSuccessor(blockMap, bci, handler);
+ }
+ }
+ }
+ stream.next();
+ }
+ }
+
+ private BciBlock makeBlock(BciBlock[] blockMap, int startBci) {
+ BciBlock oldBlock = blockMap[startBci];
+ if (oldBlock == null) {
+ BciBlock newBlock = new BciBlock();
+ blocksNotYetAssignedId++;
+ newBlock.startBci = startBci;
+ blockMap[startBci] = newBlock;
+ return newBlock;
+
+ } else if (oldBlock.startBci != startBci) {
+ // Backward branch into the middle of an already processed block.
+ // Add the correct fall-through successor.
+ BciBlock newBlock = new BciBlock();
+ blocksNotYetAssignedId++;
+ newBlock.startBci = startBci;
+ newBlock.endBci = oldBlock.endBci;
+ for (BciBlock oldSuccessor : oldBlock.getSuccessors()) {
+ newBlock.addSuccessor(oldSuccessor);
+ }
+
+ oldBlock.endBci = startBci - 1;
+ oldBlock.clearSucccessors();
+ oldBlock.addSuccessor(newBlock);
+
+ for (int i = startBci; i <= newBlock.endBci; i++) {
+ blockMap[i] = newBlock;
+ }
+ return newBlock;
+
+ } else {
+ return oldBlock;
+ }
+ }
+
+ private void addSwitchSuccessors(BciBlock[] blockMap, int predBci, BytecodeSwitch bswitch) {
+ // adds distinct targets to the successor list
+ Collection targets = new TreeSet<>();
+ for (int i = 0; i < bswitch.numberOfCases(); i++) {
+ targets.add(bswitch.targetAt(i));
+ }
+ targets.add(bswitch.defaultTarget());
+ for (int targetBci : targets) {
+ addSuccessor(blockMap, predBci, makeBlock(blockMap, targetBci));
+ }
+ }
+
+ private static void addSuccessor(BciBlock[] blockMap, int predBci, BciBlock sux) {
+ BciBlock predecessor = blockMap[predBci];
+ if (sux.isExceptionEntry) {
+ throw new PermanentBailoutException("Exception handler can be reached by both normal and exceptional control flow");
+ }
+ predecessor.addSuccessor(sux);
+ }
+
+ private final ArrayList jsrVisited = new ArrayList<>();
+
+ private void createJsrAlternatives(BciBlock[] blockMap, BciBlock block) {
+ jsrVisited.add(block);
+ JsrScope scope = block.getJsrScope();
+
+ if (block.endsWithRet()) {
+ block.setRetSuccessor(blockMap[scope.nextReturnAddress()]);
+ block.addSuccessor(block.getRetSuccessor());
+ assert block.getRetSuccessor() != block.getJsrSuccessor();
+ }
+ Debug.log("JSR alternatives block %s sux %s jsrSux %s retSux %s jsrScope %s", block, block.getSuccessors(), block.getJsrSuccessor(), block.getRetSuccessor(), block.getJsrScope());
+
+ if (block.getJsrSuccessor() != null || !scope.isEmpty()) {
+ for (int i = 0; i < block.getSuccessorCount(); i++) {
+ BciBlock successor = block.getSuccessor(i);
+ JsrScope nextScope = scope;
+ if (successor == block.getJsrSuccessor()) {
+ nextScope = scope.push(block.getJsrReturnBci());
+ }
+ if (successor == block.getRetSuccessor()) {
+ nextScope = scope.pop();
+ }
+ if (!successor.getJsrScope().isPrefixOf(nextScope)) {
+ throw new JsrNotSupportedBailout("unstructured control flow (" + successor.getJsrScope() + " " + nextScope + ")");
+ }
+ if (!nextScope.isEmpty()) {
+ BciBlock clone;
+ if (successor.getJsrAlternatives() != null && successor.getJsrAlternatives().containsKey(nextScope)) {
+ clone = successor.getJsrAlternatives().get(nextScope);
+ } else {
+ successor.initJsrAlternatives();
+ clone = successor.copy();
+ blocksNotYetAssignedId++;
+ clone.setJsrScope(nextScope);
+ successor.getJsrAlternatives().put(nextScope, clone);
+ }
+ block.getSuccessors().set(i, clone);
+ if (successor == block.getJsrSuccessor()) {
+ block.setJsrSuccessor(clone);
+ }
+ if (successor == block.getRetSuccessor()) {
+ block.setRetSuccessor(clone);
+ }
+ }
+ }
+ }
+ for (BciBlock successor : block.getSuccessors()) {
+ if (!jsrVisited.contains(successor)) {
+ createJsrAlternatives(blockMap, successor);
+ }
+ }
+ }
+
+ private HashMap initialExceptionDispatch = CollectionsFactory.newMap();
+
+ private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, int bci) {
+ ExceptionDispatchBlock lastHandler = null;
+
+ for (int i = exceptionHandlers.length - 1; i >= 0; i--) {
+ ExceptionHandler h = exceptionHandlers[i];
+ if (h.getStartBCI() <= bci && bci < h.getEndBCI()) {
+ if (h.isCatchAll()) {
+ // Discard all information about succeeding exception handlers, since they can
+ // never be reached.
+ lastHandler = null;
+ }
+
+ HashMap exceptionDispatch = lastHandler != null ? lastHandler.exceptionDispatch : initialExceptionDispatch;
+ ExceptionDispatchBlock curHandler = exceptionDispatch.get(h);
+ if (curHandler == null) {
+ curHandler = new ExceptionDispatchBlock();
+ blocksNotYetAssignedId++;
+ curHandler.startBci = -1;
+ curHandler.endBci = -1;
+ curHandler.deoptBci = bci;
+ curHandler.handler = h;
+ curHandler.addSuccessor(blockMap[h.getHandlerBCI()]);
+ if (lastHandler != null) {
+ curHandler.addSuccessor(lastHandler);
+ }
+ exceptionDispatch.put(h, curHandler);
+ }
+ lastHandler = curHandler;
+ }
+ }
+ return lastHandler;
+ }
+
+ private boolean loopChanges;
+
+ private void fixLoopBits(BciBlock[] blockMap) {
+ do {
+ loopChanges = false;
+ for (BciBlock b : blocks) {
+ b.visited = false;
+ }
+
+ long loop = fixLoopBits(blockMap, blockMap[0]);
+
+ if (loop != 0) {
+ // There is a path from a loop end to the method entry that does not pass the loop
+ // header.
+ // Therefore, the loop is non reducible (has more than one entry).
+ // We don't want to compile such methods because the IR only supports structured
+ // loops.
+ throw new PermanentBailoutException("Non-reducible loop: %016x", loop);
+ }
+ } while (loopChanges);
+ }
+
+ private void computeBlockOrder(BciBlock[] blockMap) {
+ int maxBlocks = blocksNotYetAssignedId;
+ this.blocks = new BciBlock[blocksNotYetAssignedId];
+ long loop = computeBlockOrder(blockMap[0]);
+
+ if (loop != 0) {
+ // There is a path from a loop end to the method entry that does not pass the loop
+ // header. Therefore, the loop is non reducible (has more than one entry).
+ // We don't want to compile such methods because the IR only supports structured loops.
+ throw new PermanentBailoutException("Non-reducible loop");
+ }
+
+ // Purge null entries for unreached blocks and sort blocks such that loop bodies are always
+ // consecutively in the array.
+ int blockCount = maxBlocks - blocksNotYetAssignedId + 2;
+ BciBlock[] newBlocks = new BciBlock[blockCount];
+ int next = 0;
+ for (int i = 0; i < blocks.length; ++i) {
+ BciBlock b = blocks[i];
+ if (b != null) {
+ b.setId(next);
+ newBlocks[next++] = b;
+ if (b.isLoopHeader) {
+ next = handleLoopHeader(newBlocks, next, i, b);
+ }
+ }
+ }
+
+ // Add return block.
+ BciBlock returnBlock = new BciBlock();
+ returnBlock.startBci = returnBci;
+ returnBlock.endBci = returnBci;
+ returnBlock.setId(newBlocks.length - 2);
+ newBlocks[newBlocks.length - 2] = returnBlock;
+
+ // Add unwind block.
+ ExceptionDispatchBlock unwindBlock = new ExceptionDispatchBlock();
+ unwindBlock.startBci = -1;
+ unwindBlock.endBci = -1;
+ unwindBlock.deoptBci = code.getMethod().isSynchronized() ? BytecodeFrame.UNWIND_BCI : BytecodeFrame.AFTER_EXCEPTION_BCI;
+ unwindBlock.setId(newBlocks.length - 1);
+ newBlocks[newBlocks.length - 1] = unwindBlock;
+
+ blocks = newBlocks;
+ }
+
+ private int handleLoopHeader(BciBlock[] newBlocks, int nextStart, int i, BciBlock loopHeader) {
+ int next = nextStart;
+ int endOfLoop = nextStart - 1;
+ for (int j = i + 1; j < blocks.length; ++j) {
+ BciBlock other = blocks[j];
+ if (other != null && (other.loops & (1L << loopHeader.loopId)) != 0) {
+ other.setId(next);
+ endOfLoop = next;
+ newBlocks[next++] = other;
+ blocks[j] = null;
+ if (other.isLoopHeader) {
+ next = handleLoopHeader(newBlocks, next, j, other);
+ }
+ }
+ }
+ loopHeader.loopEnd = endOfLoop;
+ return next;
+ }
+
+ public void log(BciBlock[] blockMap, String name) {
+ if (Debug.isLogEnabled()) {
+ String n = System.lineSeparator();
+ StringBuilder sb = new StringBuilder(Debug.currentScope()).append("BlockMap ").append(name).append(" :");
+ sb.append(n);
+ Iterable it;
+ if (blocks == null) {
+ it = new HashSet<>(Arrays.asList(blockMap));
+ } else {
+ it = Arrays.asList(blocks);
+ }
+ for (BciBlock b : it) {
+ if (b == null) {
+ continue;
+ }
+ sb.append("B").append(b.getId()).append(" (").append(b.startBci).append(" -> ").append(b.endBci).append(")");
+ if (b.isLoopHeader) {
+ sb.append(" LoopHeader");
+ }
+ if (b.isExceptionEntry) {
+ sb.append(" ExceptionEntry");
+ }
+ sb.append(n).append(" Sux : ");
+ for (BciBlock s : b.getSuccessors()) {
+ sb.append("B").append(s.getId()).append(" (").append(s.startBci).append(" -> ").append(s.endBci).append(")");
+ if (s.isExceptionEntry) {
+ sb.append("!");
+ }
+ sb.append(" ");
+ }
+ sb.append(n).append(" Loop : ");
+ for (int pos : b.loopIdIterable()) {
+ sb.append("B").append(loopHeaders[pos].getId()).append(" ");
+ }
+ sb.append(n);
+ }
+ Debug.log("%s", sb);
+ }
+ }
+
+ /**
+ * Get the header block for a loop index.
+ */
+ public BciBlock getLoopHeader(int index) {
+ return loopHeaders[index];
+ }
+
+ /**
+ * The next available loop number.
+ */
+ private int nextLoop;
+
+ /**
+ * Mark the block as a loop header, using the next available loop number. Also checks for corner
+ * cases that we don't want to compile.
+ */
+ private void makeLoopHeader(BciBlock block) {
+ if (!block.isLoopHeader) {
+ block.isLoopHeader = true;
+
+ if (block.isExceptionEntry) {
+ // Loops that are implicitly formed by an exception handler lead to all sorts of
+ // corner cases.
+ // Don't compile such methods for now, until we see a concrete case that allows
+ // checking for correctness.
+ throw new PermanentBailoutException("Loop formed by an exception handler");
+ }
+ if (nextLoop >= LOOP_HEADER_MAX_CAPACITY) {
+ // This restriction can be removed by using a fall-back to a BitSet in case we have
+ // more than 64 loops
+ // Don't compile such methods for now, until we see a concrete case that allows
+ // checking for correctness.
+ throw new PermanentBailoutException("Too many loops in method");
+ }
+
+ assert block.loops == 0;
+ block.loops = 1L << nextLoop;
+ Debug.log("makeLoopHeader(%s) -> %x", block, block.loops);
+ if (loopHeaders == null) {
+ loopHeaders = new BciBlock[LOOP_HEADER_INITIAL_CAPACITY];
+ } else if (nextLoop >= loopHeaders.length) {
+ loopHeaders = Arrays.copyOf(loopHeaders, LOOP_HEADER_MAX_CAPACITY);
+ }
+ loopHeaders[nextLoop] = block;
+ block.loopId = nextLoop;
+ nextLoop++;
+ }
+ assert Long.bitCount(block.loops) == 1;
+ }
+
+ /**
+ * Depth-first traversal of the control flow graph. The flag {@linkplain BciBlock#visited} is
+ * used to visit every block only once. The flag {@linkplain BciBlock#active} is used to detect
+ * cycles (backward edges).
+ */
+ private long computeBlockOrder(BciBlock block) {
+ if (block.visited) {
+ if (block.active) {
+ // Reached block via backward branch.
+ makeLoopHeader(block);
+ // Return cached loop information for this block.
+ return block.loops;
+ } else if (block.isLoopHeader) {
+ return block.loops & ~(1L << block.loopId);
+ } else {
+ return block.loops;
+ }
+ }
+
+ block.visited = true;
+ block.active = true;
+
+ long loops = 0;
+ for (BciBlock successor : block.getSuccessors()) {
+ // Recursively process successors.
+ loops |= computeBlockOrder(successor);
+ if (successor.active) {
+ // Reached block via backward branch.
+ loops |= (1L << successor.loopId);
+ }
+ }
+
+ block.loops = loops;
+ Debug.log("computeBlockOrder(%s) -> %x", block, block.loops);
+
+ if (block.isLoopHeader) {
+ loops &= ~(1L << block.loopId);
+ }
+
+ block.active = false;
+ blocksNotYetAssignedId--;
+ blocks[blocksNotYetAssignedId] = block;
+
+ return loops;
+ }
+
+ private long fixLoopBits(BciBlock[] blockMap, BciBlock block) {
+ if (block.visited) {
+ // Return cached loop information for this block.
+ if (block.isLoopHeader) {
+ return block.loops & ~(1L << block.loopId);
+ } else {
+ return block.loops;
+ }
+ }
+
+ block.visited = true;
+ long loops = block.loops;
+ for (BciBlock successor : block.getSuccessors()) {
+ // Recursively process successors.
+ loops |= fixLoopBits(blockMap, successor);
+ }
+ if (block.loops != loops) {
+ loopChanges = true;
+ block.loops = loops;
+ Debug.log("fixLoopBits0(%s) -> %x", block, block.loops);
+ }
+
+ if (block.isLoopHeader) {
+ loops &= ~(1L << block.loopId);
+ }
+
+ return loops;
+ }
+
+ public static BciBlockMapping create(BytecodeStream stream, Bytecode code) {
+ BciBlockMapping map = new BciBlockMapping(code);
+ map.build(stream);
+ if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+ Debug.dump(Debug.INFO_LOG_LEVEL, map, code.getMethod().format("After block building %f %R %H.%n(%P)"));
+ }
+
+ return map;
+ }
+
+ public BciBlock[] getLoopHeaders() {
+ return loopHeaders;
+ }
+
+ public BciBlock getStartBlock() {
+ return startBlock;
+ }
+
+ public BciBlock getReturnBlock() {
+ return blocks[blocks.length - 2];
+ }
+
+ public ExceptionDispatchBlock getUnwindBlock() {
+ return (ExceptionDispatchBlock) blocks[blocks.length - 1];
+ }
+
+ public int getLoopCount() {
+ return nextLoop;
+ }
+
+ public int getBlockCount() {
+ return blocks.length;
+ }
+}