# HG changeset patch # User Thomas Wuerthinger # Date 1325611330 -3600 # Node ID 2af849af1723411c4f63a9bffab0f69283540939 # Parent 4df4499e0289185e93e685b492528051068419b2 moved graphbuilder into the graal.java project. diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Tue Jan 03 18:22:10 2012 +0100 @@ -40,7 +40,6 @@ import com.oracle.max.graal.compiler.*; import com.oracle.max.graal.compiler.alloc.*; import com.oracle.max.graal.compiler.alloc.OperandPool.VariableFlag; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.lir.*; import com.oracle.max.graal.compiler.schedule.*; import com.oracle.max.graal.compiler.stub.*; @@ -695,15 +694,25 @@ if (stackIndex == 0 && !isStatic) { // Current argument is receiver. - stackIndex += FrameStateBuilder.stackSlots(CiKind.Object); + stackIndex += stackSlots(CiKind.Object); } else { - stackIndex += FrameStateBuilder.stackSlots(signature.argumentKindAt(argumentIndex, false)); + stackIndex += stackSlots(signature.argumentKindAt(argumentIndex, false)); argumentIndex++; } } return stack; } + + public static int stackSlots(CiKind kind) { + return isTwoSlot(kind) ? 2 : 1; + } + + public static boolean isTwoSlot(CiKind kind) { + assert kind != CiKind.Void && kind != CiKind.Illegal; + return kind == CiKind.Long || kind == CiKind.Double; + } + @Override public void emitInvoke(Invoke x) { MethodCallTargetNode callTarget = x.callTarget(); diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BlockMap.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BlockMap.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,631 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import static com.oracle.max.graal.compiler.graphbuilder.Bytecodes.*; - -import java.util.*; - -import com.oracle.max.cri.ci.*; -import com.oracle.max.cri.ri.*; -import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.nodes.*; - -/** - * 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 BlockMap#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. - */ -public final class BlockMap { - - public static class Block implements Cloneable { - public int startBci; - public int endBci; - public boolean isExceptionEntry; - public boolean isLoopHeader; - public int blockID; - - public FixedWithNextNode firstInstruction; - - public ArrayList successors = new ArrayList<>(2); - public int normalSuccessors; - - private boolean visited; - private boolean active; - public long loops; - - public HashMap jsrAlternatives; - public JsrScope jsrScope = JsrScope.EMPTY_SCOPE; - public Block jsrSuccessor; - public int jsrReturnBci; - public Block retSuccessor; - public boolean endsWithRet = false; - - public Block copy() { - try { - Block block = (Block) super.clone(); - block.successors = new ArrayList<>(successors); - return block; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - } - - public static class ExceptionBlock extends Block { - public RiExceptionHandler handler; - public int deoptBci; - } - - public static class DeoptBlock extends Block { - public DeoptBlock(int startBci) { - this.startBci = startBci; - } - } - - /** - * The blocks found in this method, in reverse postorder. - */ - public final List blocks; - - public final RiResolvedMethod method; - - private final RiExceptionHandler[] exceptionHandlers; - - private Block[] blockMap; - - public final BitSet canTrap; - - public boolean hasJsrBytecodes; - - public Block startBlock; - - public final boolean useBranchPrediction; - - /** - * Creates a new BlockMap instance from bytecode of the given method . - * @param method the compiler interface method containing the code - */ - public BlockMap(RiResolvedMethod method, boolean useBranchPrediction) { - this.method = method; - exceptionHandlers = method.exceptionHandlers(); - this.blockMap = new Block[method.codeSize()]; - this.canTrap = new BitSet(blockMap.length); - this.blocks = new ArrayList<>(); - this.useBranchPrediction = useBranchPrediction; - } - - 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"); - } - createJsrAlternatives(blockMap[0]); - } - computeBlockOrder(); - - initializeBlockIds(); - - startBlock = blockMap[0]; - - // Discard big arrays so that they can be GCed - blockMap = null; - } - - private void initializeBlockIds() { - for (int i = 0; i < blocks.size(); i++) { - blocks.get(i).blockID = i; - } - } - - private void makeExceptionEntries() { - // start basic blocks at all exception handler blocks and mark them as exception entries - for (RiExceptionHandler h : this.exceptionHandlers) { - Block xhandler = makeBlock(h.handlerBCI()); - xhandler.isExceptionEntry = true; - } - } - - private void iterateOverBytecodes() { - // 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) - byte[] code = method.code(); - Block current = null; - int bci = 0; - while (bci < code.length) { - if (current == null || blockMap[bci] != null) { - Block b = makeBlock(bci); - if (current != null) { - setSuccessors(current.endBci, b); - } - current = b; - } - blockMap[bci] = current; - current.endBci = bci; - - int opcode = Bytes.beU1(code, bci); - switch (opcode) { - case IRETURN: // fall through - case LRETURN: // fall through - case FRETURN: // fall through - case DRETURN: // fall through - case ARETURN: // fall through - case RETURN: { - current = null; - break; - } - case ATHROW: { - current = null; - canTrap.set(bci); - 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; - double probability = useBranchPrediction ? method.branchProbability(bci) : -1; - - Block b1 = probability == 0.0 ? new DeoptBlock(bci + Bytes.beS2(code, bci + 1)) : makeBlock(bci + Bytes.beS2(code, bci + 1)); - Block b2 = probability == 1.0 ? new DeoptBlock(bci + 3) : makeBlock(bci + 3); - setSuccessors(bci, b1, b2); - break; - } - case GOTO: - case GOTO_W: { - current = null; - int target = bci + Bytes.beSVar(code, bci + 1, opcode == GOTO_W); - Block b1 = makeBlock(target); - setSuccessors(bci, b1); - break; - } - case TABLESWITCH: { - current = null; - BytecodeTableSwitch sw = new BytecodeTableSwitch(code, bci); - setSuccessors(bci, makeSwitchSuccessors(sw)); - break; - } - case LOOKUPSWITCH: { - current = null; - BytecodeLookupSwitch sw = new BytecodeLookupSwitch(code, bci); - setSuccessors(bci, makeSwitchSuccessors(sw)); - break; - } - case JSR: - case JSR_W: { - hasJsrBytecodes = true; - int target = bci + Bytes.beSVar(code, bci + 1, opcode == JSR_W); - if (target == 0) { - throw new JsrNotSupportedBailout("jsr target bci 0 not allowed"); - } - Block b1 = makeBlock(target); - current.jsrSuccessor = b1; - current.jsrReturnBci = bci + lengthOf(opcode); - current = null; - setSuccessors(bci, b1); - break; - } - case RET: { - current.endsWithRet = true; - current = null; - break; - } - case WIDE: { - int opcode2 = Bytes.beU1(code, bci); - switch (opcode2) { - case RET: { - current.endsWithRet = true; - current = null; - break; - } - } - break; - } - case INVOKEINTERFACE: - case INVOKESPECIAL: - case INVOKESTATIC: - case INVOKEVIRTUAL: { - current = null; - int target = bci + lengthOf(code, bci); - Block b1 = makeBlock(target); - setSuccessors(bci, b1); - canTrap.set(bci); - break; - } - default: { - if (canTrap(opcode, bci)) { - canTrap.set(bci); - } - } - } - bci += lengthOf(code, bci); - } - } - - public boolean canTrap(int opcode, int bci) { - switch (opcode) { - case INVOKESTATIC: - case INVOKESPECIAL: - case INVOKEVIRTUAL: - case INVOKEINTERFACE: { - return true; - } - 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 PUTFIELD: - case GETFIELD: { - if (GraalOptions.AllowExplicitExceptionChecks) { - return method.exceptionProbability(bci) > 0; - } - } - } - return false; - } - - private Block makeBlock(int startBci) { - Block oldBlock = blockMap[startBci]; - if (oldBlock == null) { - Block newBlock = new Block(); - 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. - Block newBlock = new Block(); - 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; - } - return newBlock; - - } else { - return oldBlock; - } - } - - 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)); - } - successors[max] = makeBlock(tswitch.defaultTarget()); - return successors; - } - - private void setSuccessors(int predBci, Block... successors) { - 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); - } - predecessor.normalSuccessors = successors.length; - } - - private final HashSet jsrVisited = new HashSet<>(); - - private void createJsrAlternatives(Block block) { - jsrVisited.add(block); - JsrScope scope = block.jsrScope; - - if (block.endsWithRet) { - block.retSuccessor = blockMap[scope.nextReturnAddress()]; - block.successors.add(block.retSuccessor); - assert block.retSuccessor != block.jsrSuccessor; - } - - if (block.jsrSuccessor != null || !scope.isEmpty()) { - for (int i = 0; i < block.successors.size(); i++) { - Block successor = block.successors.get(i); - JsrScope nextScope = scope; - if (successor == block.jsrSuccessor) { - nextScope = scope.push(block.jsrReturnBci); - } - if (successor == block.retSuccessor) { - nextScope = scope.pop(); - } - if (!successor.jsrScope.isEmpty()) { - throw new JsrNotSupportedBailout("unstructured control flow (" + successor.jsrScope + " " + nextScope + ")"); - } - if (!nextScope.isEmpty()) { - Block clone; - if (successor.jsrAlternatives != null && successor.jsrAlternatives.containsKey(nextScope)) { - clone = successor.jsrAlternatives.get(nextScope); - } else { - if (successor.jsrAlternatives == null) { - successor.jsrAlternatives = new HashMap<>(); - } - clone = successor.copy(); - clone.jsrScope = nextScope; - successor.jsrAlternatives.put(nextScope, clone); - } - block.successors.set(i, clone); - if (successor == block.jsrSuccessor) { - block.jsrSuccessor = clone; - } - if (successor == block.retSuccessor) { - block.retSuccessor = clone; - } - } - } - } - for (Block successor : block.successors) { - if (!jsrVisited.contains(successor)) { - createJsrAlternatives(successor); - } - } - } - - private HashMap exceptionDispatch = new HashMap<>(); - - 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<>(); - } - handlers.add(h); - if (h.isCatchAll()) { - break; - } - } - } - if (handlers != null) { - Block dispatch = makeExceptionDispatch(handlers, 0, bci); - block.successors.add(dispatch); - } - } - } - - private void computeBlockOrder() { - 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 CiBailout("Non-reducible loop"); - } - - // Convert postorder to the desired reverse postorder. - Collections.reverse(blocks); - } - - /** - * 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(Block 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 CiBailout("Loop formed by an exception handler"); - } - if (nextLoop >= Long.SIZE) { - // This restriction can be removed by using a fall-back to a BitSet in case we have more than 32 loops - // Don't compile such methods for now, until we see a concrete case that allows checking for correctness. - throw new CiBailout("Too many loops in method"); - } - - assert block.loops == 0; - block.loops = (long) 1 << (long) nextLoop; - nextLoop++; - } - assert Long.bitCount(block.loops) == 1; - } - - /** - * Depth-first traversal of the control flow graph. The flag {@linkplain Block#visited} is used to - * visit every block only once. The flag {@linkplain Block#active} is used to detect cycles (backward - * edges). - */ - private long computeBlockOrder(Block block) { - if (block.visited) { - if (block.active) { - // Reached block via backward branch. - makeLoopHeader(block); - } - // Return cached loop information for this block. - return block.loops; - } - - block.visited = true; - block.active = true; - - int loops = 0; - for (Block successor : block.successors) { - // Recursively process successors. - loops |= computeBlockOrder(successor); - } - - if (block.isLoopHeader) { - assert Long.bitCount(block.loops) == 1; - loops &= ~block.loops; - } - - block.loops = loops; - block.active = false; - blocks.add(block); - - return loops; - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeLookupSwitch.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeLookupSwitch.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -/** - * A utility for processing {@link Bytecodes#LOOKUPSWITCH} bytecodes. - */ -class BytecodeLookupSwitch extends BytecodeSwitch { - private static final int OFFSET_TO_NUMBER_PAIRS = 4; - private static final int OFFSET_TO_FIRST_PAIR_MATCH = 8; - private static final int OFFSET_TO_FIRST_PAIR_OFFSET = 12; - private static final int PAIR_SIZE = 8; - - /** - * Constructor for a {@link BytecodeStream}. - * @param stream the {@code BytecodeStream} containing the switch instruction - * @param bci the index in the stream of the switch instruction - */ - public BytecodeLookupSwitch(BytecodeStream stream, int bci) { - super(stream, bci); - } - - /** - * Constructor for a bytecode array. - * @param code the bytecode array containing the switch instruction. - * @param bci the index in the array of the switch instruction - */ - public BytecodeLookupSwitch(byte[] code, int bci) { - super(code, bci); - } - - @Override - public int defaultOffset() { - return readWord(alignedBci); - } - - @Override - public int offsetAt(int i) { - return readWord(alignedBci + OFFSET_TO_FIRST_PAIR_OFFSET + PAIR_SIZE * i); - } - - @Override - public int keyAt(int i) { - return readWord(alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * i); - } - - @Override - public int numberOfCases() { - return readWord(alignedBci + OFFSET_TO_NUMBER_PAIRS); - } - - @Override - public int size() { - return alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * numberOfCases() - bci; - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeStream.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeStream.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -/** - * A utility class that makes iterating over bytecodes and reading operands - * simpler and less error prone. For example, it handles the {@link Bytecodes#WIDE} instruction - * and wide variants of instructions internally. - */ -public final class BytecodeStream { - - private final byte[] code; - private int opcode; - private int curBCI; - private int nextBCI; - - /** - * Creates a new {@code BytecodeStream} for the specified bytecode. - * @param code the array of bytes that contains the bytecode - */ - public BytecodeStream(byte[] code) { - assert code != null; - this.code = code; - setBCI(0); - } - - /** - * Advances to the next bytecode. - */ - public void next() { - setBCI(nextBCI); - } - - /** - * Gets the next bytecode index (no side-effects). - * @return the next bytecode index - */ - public int nextBCI() { - return nextBCI; - } - - /** - * Gets the current bytecode index. - * @return the current bytecode index - */ - public int currentBCI() { - return curBCI; - } - - /** - * Gets the bytecode index of the end of the code. - * @return the index of the end of the code - */ - public int endBCI() { - return code.length; - } - - /** - * Gets the current opcode. This method will never return the - * {@link Bytecodes#WIDE WIDE} opcode, but will instead - * return the opcode that is modified by the {@code WIDE} opcode. - * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code - */ - public int currentBC() { - if (opcode == Bytecodes.WIDE) { - return Bytes.beU1(code, curBCI + 1); - } else { - return opcode; - } - } - - /** - * Reads the index of a local variable for one of the load or store instructions. - * The WIDE modifier is handled internally. - * @return the index of the local variable - */ - public int readLocalIndex() { - // read local variable index for load/store - if (opcode == Bytecodes.WIDE) { - return Bytes.beU2(code, curBCI + 2); - } - return Bytes.beU1(code, curBCI + 1); - } - - /** - * Read the delta for an {@link Bytecodes#IINC} bytecode. - * @return the delta for the {@code IINC} - */ - public int readIncrement() { - // read the delta for the iinc bytecode - if (opcode == Bytecodes.WIDE) { - return Bytes.beS2(code, curBCI + 4); - } - return Bytes.beS1(code, curBCI + 2); - } - - /** - * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions. - * @return the destination bytecode index - */ - public int readBranchDest() { - // reads the destination for a branch bytecode - return curBCI + Bytes.beS2(code, curBCI + 1); - } - - /** - * Read the destination of a {@link Bytecodes#GOTO_W} or {@link Bytecodes#JSR_W} instructions. - * @return the destination bytecode index - */ - public int readFarBranchDest() { - // reads the destination for a wide branch bytecode - return curBCI + Bytes.beS4(code, curBCI + 1); - } - - /** - * Read a signed 4-byte integer from the bytecode stream at the specified bytecode index. - * @param bci the bytecode index - * @return the integer value - */ - public int readInt(int bci) { - // reads a 4-byte signed value - return Bytes.beS4(code, bci); - } - - /** - * Reads an unsigned, 1-byte value from the bytecode stream at the specified bytecode index. - * @param bci the bytecode index - * @return the byte - */ - public int readUByte(int bci) { - return Bytes.beU1(code, bci); - } - - /** - * Reads a constant pool index for the current instruction. - * @return the constant pool index - */ - public char readCPI() { - if (opcode == Bytecodes.LDC) { - return (char) Bytes.beU1(code, curBCI + 1); - } - return (char) Bytes.beU2(code, curBCI + 1); - } - - /** - * Reads a signed, 1-byte value for the current instruction (e.g. BIPUSH). - * @return the byte - */ - public byte readByte() { - return code[curBCI + 1]; - } - - /** - * Reads a signed, 2-byte short for the current instruction (e.g. SIPUSH). - * @return the short value - */ - public short readShort() { - return (short) Bytes.beS2(code, curBCI + 1); - } - - /** - * Sets the bytecode index to the specified value. - * If {@code bci} is beyond the end of the array, {@link #currentBC} will return - * {@link Bytecodes#END} and other methods may throw {@link ArrayIndexOutOfBoundsException}. - * @param bci the new bytecode index - */ - public void setBCI(int bci) { - curBCI = bci; - if (curBCI < code.length) { - opcode = Bytes.beU1(code, bci); - nextBCI = bci + Bytecodes.lengthOf(code, bci); - } else { - opcode = Bytecodes.END; - nextBCI = curBCI; - } - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeSwitch.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeSwitch.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -/** - * An abstract class that provides the state and methods common to {@link Bytecodes#LOOKUPSWITCH} - * and {@link Bytecodes#TABLESWITCH} instructions. - */ -public abstract class BytecodeSwitch { - /** - * The {@link BytecodeStream} containing bytecode array or {@code null} if {@link #code} is not {@code null}. - */ - private final BytecodeStream stream; - /** - * The bytecode array or {@code null} if {@link #stream} is not {@code null}. - */ - private final byte[] code; - /** - * Index of start of switch instruction. - */ - protected final int bci; - /** - * Index of the start of the additional data for the switch instruction, aligned to a multiple of four from the method start. - */ - protected final int alignedBci; - - /** - * Constructor for a {@link BytecodeStream}. - * @param stream the {@code BytecodeStream} containing the switch instruction - * @param bci the index in the stream of the switch instruction - */ - public BytecodeSwitch(BytecodeStream stream, int bci) { - this.alignedBci = (bci + 4) & 0xfffffffc; - this.stream = stream; - this.code = null; - this.bci = bci; - } - - /** - * Constructor for a bytecode array. - * @param code the bytecode array containing the switch instruction. - * @param bci the index in the array of the switch instruction - */ - public BytecodeSwitch(byte[] code, int bci) { - this.alignedBci = (bci + 4) & 0xfffffffc; - this.stream = null; - this.code = code; - this.bci = bci; - } - - /** - * Gets the current bytecode index. - * @return the current bytecode index - */ - public int bci() { - return bci; - } - - /** - * Gets the index of the instruction denoted by the {@code i}'th switch target. - * @param i index of the switch target - * @return the index of the instruction denoted by the {@code i}'th switch target - */ - public int targetAt(int i) { - return bci + offsetAt(i); - } - - /** - * Gets the index of the instruction for the default switch target. - * @return the index of the instruction for the default switch target - */ - public int defaultTarget() { - return bci + defaultOffset(); - } - - /** - * Gets the offset from the start of the switch instruction to the default switch target. - * @return the offset to the default switch target - */ - public abstract int defaultOffset(); - - /** - * Gets the key at {@code i}'th switch target index. - * @param i the switch target index - * @return the key at {@code i}'th switch target index - */ - public abstract int keyAt(int i); - - /** - * Gets the offset from the start of the switch instruction for the {@code i}'th switch target. - * @param i the switch target index - * @return the offset to the {@code i}'th switch target - */ - public abstract int offsetAt(int i); - - /** - * Gets the number of switch targets. - * @return the number of switch targets - */ - public abstract int numberOfCases(); - - /** - * Gets the total size in bytes of the switch instruction. - * @return the total size in bytes of the switch instruction - */ - public abstract int size(); - - /** - * Reads the signed value at given bytecode index. - * @param readBci the start index of the value to retrieve - * @return the signed, 4-byte value in the bytecode array starting at {@code bci} - */ - protected int readWord(int readBci) { - if (code != null) { - return Bytes.beS4(code, readBci); - } - return stream.readInt(readBci); - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeTableSwitch.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/BytecodeTableSwitch.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -/** - * A utility for processing {@link Bytecodes#TABLESWITCH} bytecodes. - */ -public class BytecodeTableSwitch extends BytecodeSwitch { - private static final int OFFSET_TO_LOW_KEY = 4; - private static final int OFFSET_TO_HIGH_KEY = 8; - private static final int OFFSET_TO_FIRST_JUMP_OFFSET = 12; - private static final int JUMP_OFFSET_SIZE = 4; - - /** - * Constructor for a {@link BytecodeStream}. - * @param stream the {@code BytecodeStream} containing the switch instruction - * @param bci the index in the stream of the switch instruction - */ - public BytecodeTableSwitch(BytecodeStream stream, int bci) { - super(stream, bci); - } - - /** - * Constructor for a bytecode array. - * @param code the bytecode array containing the switch instruction. - * @param bci the index in the array of the switch instruction - */ - public BytecodeTableSwitch(byte[] code, int bci) { - super(code, bci); - } - - /** - * Gets the low key of the table switch. - * @return the low key - */ - public int lowKey() { - return readWord(alignedBci + OFFSET_TO_LOW_KEY); - } - - /** - * Gets the high key of the table switch. - * @return the high key - */ - public int highKey() { - return readWord(alignedBci + OFFSET_TO_HIGH_KEY); - } - - @Override - public int keyAt(int i) { - return lowKey() + i; - } - - @Override - public int defaultOffset() { - return readWord(alignedBci); - } - - @Override - public int offsetAt(int i) { - return readWord(alignedBci + OFFSET_TO_FIRST_JUMP_OFFSET + JUMP_OFFSET_SIZE * i); - } - - @Override - public int numberOfCases() { - return highKey() - lowKey() + 1; - } - - @Override - public int size() { - return alignedBci + OFFSET_TO_FIRST_JUMP_OFFSET + JUMP_OFFSET_SIZE * numberOfCases() - bci; - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/Bytecodes.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/Bytecodes.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,961 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import static com.oracle.max.graal.compiler.graphbuilder.Bytecodes.Flags.*; - -import java.io.*; -import java.lang.reflect.*; -import java.util.regex.*; - -/** - * The definitions of the bytecodes that are valid input to the compiler and - * related utility methods. This comprises two groups: the standard Java - * bytecodes defined by - * Java Virtual Machine Specification, and a set of extended - * bytecodes that support low-level programming, for example, memory barriers. - * - * The extended bytecodes are one or three bytes in size. The one-byte bytecodes - * follow the values in the standard set, with no gap. The three-byte extended - * bytecodes share a common first byte and carry additional instruction-specific - * information in the second and third bytes. - */ -public class Bytecodes { - public static final int NOP = 0; // 0x00 - public static final int ACONST_NULL = 1; // 0x01 - public static final int ICONST_M1 = 2; // 0x02 - public static final int ICONST_0 = 3; // 0x03 - public static final int ICONST_1 = 4; // 0x04 - public static final int ICONST_2 = 5; // 0x05 - public static final int ICONST_3 = 6; // 0x06 - public static final int ICONST_4 = 7; // 0x07 - public static final int ICONST_5 = 8; // 0x08 - public static final int LCONST_0 = 9; // 0x09 - public static final int LCONST_1 = 10; // 0x0A - public static final int FCONST_0 = 11; // 0x0B - public static final int FCONST_1 = 12; // 0x0C - public static final int FCONST_2 = 13; // 0x0D - public static final int DCONST_0 = 14; // 0x0E - public static final int DCONST_1 = 15; // 0x0F - public static final int BIPUSH = 16; // 0x10 - public static final int SIPUSH = 17; // 0x11 - public static final int LDC = 18; // 0x12 - public static final int LDC_W = 19; // 0x13 - public static final int LDC2_W = 20; // 0x14 - public static final int ILOAD = 21; // 0x15 - public static final int LLOAD = 22; // 0x16 - public static final int FLOAD = 23; // 0x17 - public static final int DLOAD = 24; // 0x18 - public static final int ALOAD = 25; // 0x19 - public static final int ILOAD_0 = 26; // 0x1A - public static final int ILOAD_1 = 27; // 0x1B - public static final int ILOAD_2 = 28; // 0x1C - public static final int ILOAD_3 = 29; // 0x1D - public static final int LLOAD_0 = 30; // 0x1E - public static final int LLOAD_1 = 31; // 0x1F - public static final int LLOAD_2 = 32; // 0x20 - public static final int LLOAD_3 = 33; // 0x21 - public static final int FLOAD_0 = 34; // 0x22 - public static final int FLOAD_1 = 35; // 0x23 - public static final int FLOAD_2 = 36; // 0x24 - public static final int FLOAD_3 = 37; // 0x25 - public static final int DLOAD_0 = 38; // 0x26 - public static final int DLOAD_1 = 39; // 0x27 - public static final int DLOAD_2 = 40; // 0x28 - public static final int DLOAD_3 = 41; // 0x29 - public static final int ALOAD_0 = 42; // 0x2A - public static final int ALOAD_1 = 43; // 0x2B - public static final int ALOAD_2 = 44; // 0x2C - public static final int ALOAD_3 = 45; // 0x2D - public static final int IALOAD = 46; // 0x2E - public static final int LALOAD = 47; // 0x2F - public static final int FALOAD = 48; // 0x30 - public static final int DALOAD = 49; // 0x31 - public static final int AALOAD = 50; // 0x32 - public static final int BALOAD = 51; // 0x33 - public static final int CALOAD = 52; // 0x34 - public static final int SALOAD = 53; // 0x35 - public static final int ISTORE = 54; // 0x36 - public static final int LSTORE = 55; // 0x37 - public static final int FSTORE = 56; // 0x38 - public static final int DSTORE = 57; // 0x39 - public static final int ASTORE = 58; // 0x3A - public static final int ISTORE_0 = 59; // 0x3B - public static final int ISTORE_1 = 60; // 0x3C - public static final int ISTORE_2 = 61; // 0x3D - public static final int ISTORE_3 = 62; // 0x3E - public static final int LSTORE_0 = 63; // 0x3F - public static final int LSTORE_1 = 64; // 0x40 - public static final int LSTORE_2 = 65; // 0x41 - public static final int LSTORE_3 = 66; // 0x42 - public static final int FSTORE_0 = 67; // 0x43 - public static final int FSTORE_1 = 68; // 0x44 - public static final int FSTORE_2 = 69; // 0x45 - public static final int FSTORE_3 = 70; // 0x46 - public static final int DSTORE_0 = 71; // 0x47 - public static final int DSTORE_1 = 72; // 0x48 - public static final int DSTORE_2 = 73; // 0x49 - public static final int DSTORE_3 = 74; // 0x4A - public static final int ASTORE_0 = 75; // 0x4B - public static final int ASTORE_1 = 76; // 0x4C - public static final int ASTORE_2 = 77; // 0x4D - public static final int ASTORE_3 = 78; // 0x4E - public static final int IASTORE = 79; // 0x4F - public static final int LASTORE = 80; // 0x50 - public static final int FASTORE = 81; // 0x51 - public static final int DASTORE = 82; // 0x52 - public static final int AASTORE = 83; // 0x53 - public static final int BASTORE = 84; // 0x54 - public static final int CASTORE = 85; // 0x55 - public static final int SASTORE = 86; // 0x56 - public static final int POP = 87; // 0x57 - public static final int POP2 = 88; // 0x58 - public static final int DUP = 89; // 0x59 - public static final int DUP_X1 = 90; // 0x5A - public static final int DUP_X2 = 91; // 0x5B - public static final int DUP2 = 92; // 0x5C - public static final int DUP2_X1 = 93; // 0x5D - public static final int DUP2_X2 = 94; // 0x5E - public static final int SWAP = 95; // 0x5F - public static final int IADD = 96; // 0x60 - public static final int LADD = 97; // 0x61 - public static final int FADD = 98; // 0x62 - public static final int DADD = 99; // 0x63 - public static final int ISUB = 100; // 0x64 - public static final int LSUB = 101; // 0x65 - public static final int FSUB = 102; // 0x66 - public static final int DSUB = 103; // 0x67 - public static final int IMUL = 104; // 0x68 - public static final int LMUL = 105; // 0x69 - public static final int FMUL = 106; // 0x6A - public static final int DMUL = 107; // 0x6B - public static final int IDIV = 108; // 0x6C - public static final int LDIV = 109; // 0x6D - public static final int FDIV = 110; // 0x6E - public static final int DDIV = 111; // 0x6F - public static final int IREM = 112; // 0x70 - public static final int LREM = 113; // 0x71 - public static final int FREM = 114; // 0x72 - public static final int DREM = 115; // 0x73 - public static final int INEG = 116; // 0x74 - public static final int LNEG = 117; // 0x75 - public static final int FNEG = 118; // 0x76 - public static final int DNEG = 119; // 0x77 - public static final int ISHL = 120; // 0x78 - public static final int LSHL = 121; // 0x79 - public static final int ISHR = 122; // 0x7A - public static final int LSHR = 123; // 0x7B - public static final int IUSHR = 124; // 0x7C - public static final int LUSHR = 125; // 0x7D - public static final int IAND = 126; // 0x7E - public static final int LAND = 127; // 0x7F - public static final int IOR = 128; // 0x80 - public static final int LOR = 129; // 0x81 - public static final int IXOR = 130; // 0x82 - public static final int LXOR = 131; // 0x83 - public static final int IINC = 132; // 0x84 - public static final int I2L = 133; // 0x85 - public static final int I2F = 134; // 0x86 - public static final int I2D = 135; // 0x87 - public static final int L2I = 136; // 0x88 - public static final int L2F = 137; // 0x89 - public static final int L2D = 138; // 0x8A - public static final int F2I = 139; // 0x8B - public static final int F2L = 140; // 0x8C - public static final int F2D = 141; // 0x8D - public static final int D2I = 142; // 0x8E - public static final int D2L = 143; // 0x8F - public static final int D2F = 144; // 0x90 - public static final int I2B = 145; // 0x91 - public static final int I2C = 146; // 0x92 - public static final int I2S = 147; // 0x93 - public static final int LCMP = 148; // 0x94 - public static final int FCMPL = 149; // 0x95 - public static final int FCMPG = 150; // 0x96 - public static final int DCMPL = 151; // 0x97 - public static final int DCMPG = 152; // 0x98 - public static final int IFEQ = 153; // 0x99 - public static final int IFNE = 154; // 0x9A - public static final int IFLT = 155; // 0x9B - public static final int IFGE = 156; // 0x9C - public static final int IFGT = 157; // 0x9D - public static final int IFLE = 158; // 0x9E - public static final int IF_ICMPEQ = 159; // 0x9F - public static final int IF_ICMPNE = 160; // 0xA0 - public static final int IF_ICMPLT = 161; // 0xA1 - public static final int IF_ICMPGE = 162; // 0xA2 - public static final int IF_ICMPGT = 163; // 0xA3 - public static final int IF_ICMPLE = 164; // 0xA4 - public static final int IF_ACMPEQ = 165; // 0xA5 - public static final int IF_ACMPNE = 166; // 0xA6 - public static final int GOTO = 167; // 0xA7 - public static final int JSR = 168; // 0xA8 - public static final int RET = 169; // 0xA9 - public static final int TABLESWITCH = 170; // 0xAA - public static final int LOOKUPSWITCH = 171; // 0xAB - public static final int IRETURN = 172; // 0xAC - public static final int LRETURN = 173; // 0xAD - public static final int FRETURN = 174; // 0xAE - public static final int DRETURN = 175; // 0xAF - public static final int ARETURN = 176; // 0xB0 - public static final int RETURN = 177; // 0xB1 - public static final int GETSTATIC = 178; // 0xB2 - public static final int PUTSTATIC = 179; // 0xB3 - public static final int GETFIELD = 180; // 0xB4 - public static final int PUTFIELD = 181; // 0xB5 - public static final int INVOKEVIRTUAL = 182; // 0xB6 - public static final int INVOKESPECIAL = 183; // 0xB7 - public static final int INVOKESTATIC = 184; // 0xB8 - public static final int INVOKEINTERFACE = 185; // 0xB9 - public static final int XXXUNUSEDXXX = 186; // 0xBA - public static final int NEW = 187; // 0xBB - public static final int NEWARRAY = 188; // 0xBC - public static final int ANEWARRAY = 189; // 0xBD - public static final int ARRAYLENGTH = 190; // 0xBE - public static final int ATHROW = 191; // 0xBF - public static final int CHECKCAST = 192; // 0xC0 - public static final int INSTANCEOF = 193; // 0xC1 - public static final int MONITORENTER = 194; // 0xC2 - public static final int MONITOREXIT = 195; // 0xC3 - public static final int WIDE = 196; // 0xC4 - public static final int MULTIANEWARRAY = 197; // 0xC5 - public static final int IFNULL = 198; // 0xC6 - public static final int IFNONNULL = 199; // 0xC7 - public static final int GOTO_W = 200; // 0xC8 - public static final int JSR_W = 201; // 0xC9 - public static final int BREAKPOINT = 202; // 0xCA - - public static final int ILLEGAL = 255; - public static final int END = 256; - - /** - * The last opcode defined by the JVM specification. To iterate over all JVM bytecodes: - *

-     *     for (int opcode = 0; opcode <= Bytecodes.LAST_JVM_OPCODE; ++opcode) {
-     *         //
-     *     }
-     * 
- */ - public static final int LAST_JVM_OPCODE = JSR_W; - - /** - * A collection of flags describing various bytecode attributes. - */ - static class Flags { - - /** - * Denotes an instruction that ends a basic block and does not let control flow fall through to its lexical successor. - */ - static final int STOP = 0x00000001; - - /** - * Denotes an instruction that ends a basic block and may let control flow fall through to its lexical successor. - * In practice this means it is a conditional branch. - */ - static final int FALL_THROUGH = 0x00000002; - - /** - * Denotes an instruction that has a 2 or 4 byte operand that is an offset to another instruction in the same method. - * This does not include the {@link Bytecodes#TABLESWITCH} or {@link Bytecodes#LOOKUPSWITCH} instructions. - */ - static final int BRANCH = 0x00000004; - - /** - * Denotes an instruction that reads the value of a static or instance field. - */ - static final int FIELD_READ = 0x00000008; - - /** - * Denotes an instruction that writes the value of a static or instance field. - */ - static final int FIELD_WRITE = 0x00000010; - - /** - * Denotes an instruction that is not defined in the JVM specification. - */ - static final int EXTENSION = 0x00000020; - - /** - * Denotes an instruction that can cause a trap. - */ - static final int TRAP = 0x00000080; - /** - * Denotes an instruction that is commutative. - */ - static final int COMMUTATIVE = 0x00000100; - /** - * Denotes an instruction that is associative. - */ - static final int ASSOCIATIVE = 0x00000200; - /** - * Denotes an instruction that loads an operand. - */ - static final int LOAD = 0x00000400; - /** - * Denotes an instruction that stores an operand. - */ - static final int STORE = 0x00000800; - /** - * Denotes the 4 INVOKE* instructions. - */ - static final int INVOKE = 0x00001000; - } - - // Performs a sanity check that none of the flags overlap. - static { - int allFlags = 0; - try { - for (Field field : Flags.class.getDeclaredFields()) { - int flagsFilter = Modifier.FINAL | Modifier.STATIC; - if ((field.getModifiers() & flagsFilter) == flagsFilter) { - assert field.getType() == int.class : "Only " + field; - final int flag = field.getInt(null); - assert flag != 0; - assert (flag & allFlags) == 0 : field.getName() + " has a value conflicting with another flag"; - allFlags |= flag; - } - } - } catch (Exception e) { - throw new InternalError(e.toString()); - } - } - - /** - * A array that maps from a bytecode value to a {@link String} for the corresponding instruction mnemonic. - * This will include the root instruction for the three-byte extended instructions. - */ - private static final String[] nameArray = new String[256]; - - /** - * A array that maps from a bytecode value to the set of {@link Flags} for the corresponding instruction. - */ - private static final int[] flagsArray = new int[256]; - - /** - * A array that maps from a bytecode value to the length in bytes for the corresponding instruction. - */ - private static final int[] lengthArray = new int[256]; - - // Checkstyle: stop - static { - def(NOP , "nop" , "b" ); - def(ACONST_NULL , "aconst_null" , "b" ); - def(ICONST_M1 , "iconst_m1" , "b" ); - def(ICONST_0 , "iconst_0" , "b" ); - def(ICONST_1 , "iconst_1" , "b" ); - def(ICONST_2 , "iconst_2" , "b" ); - def(ICONST_3 , "iconst_3" , "b" ); - def(ICONST_4 , "iconst_4" , "b" ); - def(ICONST_5 , "iconst_5" , "b" ); - def(LCONST_0 , "lconst_0" , "b" ); - def(LCONST_1 , "lconst_1" , "b" ); - def(FCONST_0 , "fconst_0" , "b" ); - def(FCONST_1 , "fconst_1" , "b" ); - def(FCONST_2 , "fconst_2" , "b" ); - def(DCONST_0 , "dconst_0" , "b" ); - def(DCONST_1 , "dconst_1" , "b" ); - def(BIPUSH , "bipush" , "bc" ); - def(SIPUSH , "sipush" , "bcc" ); - def(LDC , "ldc" , "bi" , TRAP); - def(LDC_W , "ldc_w" , "bii" , TRAP); - def(LDC2_W , "ldc2_w" , "bii" , TRAP); - def(ILOAD , "iload" , "bi" , LOAD); - def(LLOAD , "lload" , "bi" , LOAD); - def(FLOAD , "fload" , "bi" , LOAD); - def(DLOAD , "dload" , "bi" , LOAD); - def(ALOAD , "aload" , "bi" , LOAD); - def(ILOAD_0 , "iload_0" , "b" , LOAD); - def(ILOAD_1 , "iload_1" , "b" , LOAD); - def(ILOAD_2 , "iload_2" , "b" , LOAD); - def(ILOAD_3 , "iload_3" , "b" , LOAD); - def(LLOAD_0 , "lload_0" , "b" , LOAD); - def(LLOAD_1 , "lload_1" , "b" , LOAD); - def(LLOAD_2 , "lload_2" , "b" , LOAD); - def(LLOAD_3 , "lload_3" , "b" , LOAD); - def(FLOAD_0 , "fload_0" , "b" , LOAD); - def(FLOAD_1 , "fload_1" , "b" , LOAD); - def(FLOAD_2 , "fload_2" , "b" , LOAD); - def(FLOAD_3 , "fload_3" , "b" , LOAD); - def(DLOAD_0 , "dload_0" , "b" , LOAD); - def(DLOAD_1 , "dload_1" , "b" , LOAD); - def(DLOAD_2 , "dload_2" , "b" , LOAD); - def(DLOAD_3 , "dload_3" , "b" , LOAD); - def(ALOAD_0 , "aload_0" , "b" , LOAD); - def(ALOAD_1 , "aload_1" , "b" , LOAD); - def(ALOAD_2 , "aload_2" , "b" , LOAD); - def(ALOAD_3 , "aload_3" , "b" , LOAD); - def(IALOAD , "iaload" , "b" , TRAP); - def(LALOAD , "laload" , "b" , TRAP); - def(FALOAD , "faload" , "b" , TRAP); - def(DALOAD , "daload" , "b" , TRAP); - def(AALOAD , "aaload" , "b" , TRAP); - def(BALOAD , "baload" , "b" , TRAP); - def(CALOAD , "caload" , "b" , TRAP); - def(SALOAD , "saload" , "b" , TRAP); - def(ISTORE , "istore" , "bi" , STORE); - def(LSTORE , "lstore" , "bi" , STORE); - def(FSTORE , "fstore" , "bi" , STORE); - def(DSTORE , "dstore" , "bi" , STORE); - def(ASTORE , "astore" , "bi" , STORE); - def(ISTORE_0 , "istore_0" , "b" , STORE); - def(ISTORE_1 , "istore_1" , "b" , STORE); - def(ISTORE_2 , "istore_2" , "b" , STORE); - def(ISTORE_3 , "istore_3" , "b" , STORE); - def(LSTORE_0 , "lstore_0" , "b" , STORE); - def(LSTORE_1 , "lstore_1" , "b" , STORE); - def(LSTORE_2 , "lstore_2" , "b" , STORE); - def(LSTORE_3 , "lstore_3" , "b" , STORE); - def(FSTORE_0 , "fstore_0" , "b" , STORE); - def(FSTORE_1 , "fstore_1" , "b" , STORE); - def(FSTORE_2 , "fstore_2" , "b" , STORE); - def(FSTORE_3 , "fstore_3" , "b" , STORE); - def(DSTORE_0 , "dstore_0" , "b" , STORE); - def(DSTORE_1 , "dstore_1" , "b" , STORE); - def(DSTORE_2 , "dstore_2" , "b" , STORE); - def(DSTORE_3 , "dstore_3" , "b" , STORE); - def(ASTORE_0 , "astore_0" , "b" , STORE); - def(ASTORE_1 , "astore_1" , "b" , STORE); - def(ASTORE_2 , "astore_2" , "b" , STORE); - def(ASTORE_3 , "astore_3" , "b" , STORE); - def(IASTORE , "iastore" , "b" , TRAP); - def(LASTORE , "lastore" , "b" , TRAP); - def(FASTORE , "fastore" , "b" , TRAP); - def(DASTORE , "dastore" , "b" , TRAP); - def(AASTORE , "aastore" , "b" , TRAP); - def(BASTORE , "bastore" , "b" , TRAP); - def(CASTORE , "castore" , "b" , TRAP); - def(SASTORE , "sastore" , "b" , TRAP); - def(POP , "pop" , "b" ); - def(POP2 , "pop2" , "b" ); - def(DUP , "dup" , "b" ); - def(DUP_X1 , "dup_x1" , "b" ); - def(DUP_X2 , "dup_x2" , "b" ); - def(DUP2 , "dup2" , "b" ); - def(DUP2_X1 , "dup2_x1" , "b" ); - def(DUP2_X2 , "dup2_x2" , "b" ); - def(SWAP , "swap" , "b" ); - def(IADD , "iadd" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(LADD , "ladd" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(FADD , "fadd" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(DADD , "dadd" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(ISUB , "isub" , "b" ); - def(LSUB , "lsub" , "b" ); - def(FSUB , "fsub" , "b" ); - def(DSUB , "dsub" , "b" ); - def(IMUL , "imul" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(LMUL , "lmul" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(FMUL , "fmul" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(DMUL , "dmul" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(IDIV , "idiv" , "b" , TRAP); - def(LDIV , "ldiv" , "b" , TRAP); - def(FDIV , "fdiv" , "b" ); - def(DDIV , "ddiv" , "b" ); - def(IREM , "irem" , "b" , TRAP); - def(LREM , "lrem" , "b" , TRAP); - def(FREM , "frem" , "b" ); - def(DREM , "drem" , "b" ); - def(INEG , "ineg" , "b" ); - def(LNEG , "lneg" , "b" ); - def(FNEG , "fneg" , "b" ); - def(DNEG , "dneg" , "b" ); - def(ISHL , "ishl" , "b" ); - def(LSHL , "lshl" , "b" ); - def(ISHR , "ishr" , "b" ); - def(LSHR , "lshr" , "b" ); - def(IUSHR , "iushr" , "b" ); - def(LUSHR , "lushr" , "b" ); - def(IAND , "iand" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(LAND , "land" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(IOR , "ior" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(LOR , "lor" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(IXOR , "ixor" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(LXOR , "lxor" , "b" , COMMUTATIVE | ASSOCIATIVE); - def(IINC , "iinc" , "bic" , LOAD | STORE); - def(I2L , "i2l" , "b" ); - def(I2F , "i2f" , "b" ); - def(I2D , "i2d" , "b" ); - def(L2I , "l2i" , "b" ); - def(L2F , "l2f" , "b" ); - def(L2D , "l2d" , "b" ); - def(F2I , "f2i" , "b" ); - def(F2L , "f2l" , "b" ); - def(F2D , "f2d" , "b" ); - def(D2I , "d2i" , "b" ); - def(D2L , "d2l" , "b" ); - def(D2F , "d2f" , "b" ); - def(I2B , "i2b" , "b" ); - def(I2C , "i2c" , "b" ); - def(I2S , "i2s" , "b" ); - def(LCMP , "lcmp" , "b" ); - def(FCMPL , "fcmpl" , "b" ); - def(FCMPG , "fcmpg" , "b" ); - def(DCMPL , "dcmpl" , "b" ); - def(DCMPG , "dcmpg" , "b" ); - def(IFEQ , "ifeq" , "boo" , FALL_THROUGH | BRANCH); - def(IFNE , "ifne" , "boo" , FALL_THROUGH | BRANCH); - def(IFLT , "iflt" , "boo" , FALL_THROUGH | BRANCH); - def(IFGE , "ifge" , "boo" , FALL_THROUGH | BRANCH); - def(IFGT , "ifgt" , "boo" , FALL_THROUGH | BRANCH); - def(IFLE , "ifle" , "boo" , FALL_THROUGH | BRANCH); - def(IF_ICMPEQ , "if_icmpeq" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ICMPNE , "if_icmpne" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ICMPLT , "if_icmplt" , "boo" , FALL_THROUGH | BRANCH); - def(IF_ICMPGE , "if_icmpge" , "boo" , FALL_THROUGH | BRANCH); - def(IF_ICMPGT , "if_icmpgt" , "boo" , FALL_THROUGH | BRANCH); - def(IF_ICMPLE , "if_icmple" , "boo" , FALL_THROUGH | BRANCH); - def(IF_ACMPEQ , "if_acmpeq" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ACMPNE , "if_acmpne" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); - def(GOTO , "goto" , "boo" , STOP | BRANCH); - def(JSR , "jsr" , "boo" , STOP | BRANCH); - def(RET , "ret" , "bi" , STOP); - def(TABLESWITCH , "tableswitch" , "" , STOP); - def(LOOKUPSWITCH , "lookupswitch" , "" , STOP); - def(IRETURN , "ireturn" , "b" , TRAP | STOP); - def(LRETURN , "lreturn" , "b" , TRAP | STOP); - def(FRETURN , "freturn" , "b" , TRAP | STOP); - def(DRETURN , "dreturn" , "b" , TRAP | STOP); - def(ARETURN , "areturn" , "b" , TRAP | STOP); - def(RETURN , "return" , "b" , TRAP | STOP); - def(GETSTATIC , "getstatic" , "bjj" , TRAP | FIELD_READ); - def(PUTSTATIC , "putstatic" , "bjj" , TRAP | FIELD_WRITE); - def(GETFIELD , "getfield" , "bjj" , TRAP | FIELD_READ); - def(PUTFIELD , "putfield" , "bjj" , TRAP | FIELD_WRITE); - def(INVOKEVIRTUAL , "invokevirtual" , "bjj" , TRAP | INVOKE); - def(INVOKESPECIAL , "invokespecial" , "bjj" , TRAP | INVOKE); - def(INVOKESTATIC , "invokestatic" , "bjj" , TRAP | INVOKE); - def(INVOKEINTERFACE , "invokeinterface" , "bjja_", TRAP | INVOKE); - def(XXXUNUSEDXXX , "xxxunusedxxx" , "" ); - def(NEW , "new" , "bii" , TRAP); - def(NEWARRAY , "newarray" , "bc" , TRAP); - def(ANEWARRAY , "anewarray" , "bii" , TRAP); - def(ARRAYLENGTH , "arraylength" , "b" , TRAP); - def(ATHROW , "athrow" , "b" , TRAP | STOP); - def(CHECKCAST , "checkcast" , "bii" , TRAP); - def(INSTANCEOF , "instanceof" , "bii" , TRAP); - def(MONITORENTER , "monitorenter" , "b" , TRAP); - def(MONITOREXIT , "monitorexit" , "b" , TRAP); - def(WIDE , "wide" , "" ); - def(MULTIANEWARRAY , "multianewarray" , "biic" , TRAP); - def(IFNULL , "ifnull" , "boo" , FALL_THROUGH | BRANCH); - def(IFNONNULL , "ifnonnull" , "boo" , FALL_THROUGH | BRANCH); - def(GOTO_W , "goto_w" , "boooo", STOP | BRANCH); - def(JSR_W , "jsr_w" , "boooo", STOP | BRANCH); - def(BREAKPOINT , "breakpoint" , "b" , TRAP); - } - // Checkstyle: resume - - /** - * Determines if an opcode is commutative. - * @param opcode the opcode to check - * @return {@code true} iff commutative - */ - public static boolean isCommutative(int opcode) { - return (flagsArray[opcode & 0xff] & COMMUTATIVE) != 0; - } - - /** - * Gets the length of an instruction denoted by a given opcode. - * - * @param opcode an instruction opcode - * @return the length of the instruction denoted by {@code opcode}. If {@code opcode} is an illegal instruction or denotes a - * variable length instruction (e.g. {@link #TABLESWITCH}), then 0 is returned. - */ - public static int lengthOf(int opcode) { - return lengthArray[opcode & 0xff]; - } - - /** - * Gets the length of an instruction at a given position in a given bytecode array. - * This methods handles variable length and {@linkplain #WIDE widened} instructions. - * - * @param code an array of bytecode - * @param bci the position in {@code code} of an instruction's opcode - * @return the length of the instruction at position {@code bci} in {@code code} - */ - public static int lengthOf(byte[] code, int bci) { - int opcode = Bytes.beU1(code, bci); - int length = Bytecodes.lengthArray[opcode & 0xff]; - if (length == 0) { - switch (opcode) { - case TABLESWITCH: { - return new BytecodeTableSwitch(code, bci).size(); - } - case LOOKUPSWITCH: { - return new BytecodeLookupSwitch(code, bci).size(); - } - case WIDE: { - int opc = Bytes.beU1(code, bci + 1); - if (opc == RET) { - return 4; - } else if (opc == IINC) { - return 6; - } else { - return 4; // a load or store bytecode - } - } - default: - throw new Error("unknown variable-length bytecode: " + opcode); - } - } - return length; - } - - /** - * Gets the lower-case mnemonic for a given opcode. - * - * @param opcode an opcode - * @return the mnemonic for {@code opcode} or {@code ""} if {@code opcode} is not a legal opcode - */ - public static String nameOf(int opcode) throws IllegalArgumentException { - String name = nameArray[opcode & 0xff]; - if (name == null) { - return ""; - } - return name; - } - - /** - * Allocation-free version of {@linkplain #nameOf(int)}. - * @param opcode an opcode. - * @return the mnemonic for {@code opcode} or {@code ""} if {@code opcode} is not a legal opcode. - */ - public static String baseNameOf(int opcode) { - String name = nameArray[opcode & 0xff]; - if (name == null) { - return ""; - } - return name; - } - - /** - * Gets the opcode corresponding to a given mnemonic. - * - * @param name an opcode mnemonic - * @return the opcode corresponding to {@code mnemonic} - * @throws IllegalArgumentException if {@code name} does not denote a valid opcode - */ - public static int valueOf(String name) { - for (int opcode = 0; opcode < nameArray.length; ++opcode) { - if (name.equalsIgnoreCase(nameArray[opcode])) { - return opcode; - } - } - throw new IllegalArgumentException("No opcode for " + name); - } - - /** - * Determines if a given opcode denotes an instruction that can cause an implicit exception. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} can cause an implicit exception, {@code false} otherwise - */ - public static boolean canTrap(int opcode) { - return (flagsArray[opcode & 0xff] & TRAP) != 0; - } - - /** - * Determines if a given opcode denotes an instruction that loads a local variable to the operand stack. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} loads a local variable to the operand stack, {@code false} otherwise - */ - public static boolean isLoad(int opcode) { - return (flagsArray[opcode & 0xff] & LOAD) != 0; - } - - /** - * Determines if a given opcode denotes an instruction that ends a basic block and does not let control flow fall - * through to its lexical successor. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} properly ends a basic block - */ - public static boolean isStop(int opcode) { - return (flagsArray[opcode & 0xff] & STOP) != 0; - } - - /** - * Determines if a given opcode denotes an instruction that stores a value to a local variable - * after popping it from the operand stack. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} stores a value to a local variable, {@code false} otherwise - */ - public static boolean isInvoke(int opcode) { - return (flagsArray[opcode & 0xff] & INVOKE) != 0; - } - - /** - * Determines if a given opcode denotes an instruction that stores a value to a local variable - * after popping it from the operand stack. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} stores a value to a local variable, {@code false} otherwise - */ - public static boolean isStore(int opcode) { - return (flagsArray[opcode & 0xff] & STORE) != 0; - } - - /** - * Determines if a given opcode is an instruction that delimits a basic block. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} delimits a basic block - */ - public static boolean isBlockEnd(int opcode) { - return (flagsArray[opcode & 0xff] & (STOP | FALL_THROUGH)) != 0; - } - - /** - * Determines if a given opcode is an instruction that has a 2 or 4 byte operand that is an offset to another - * instruction in the same method. This does not include the {@linkplain #TABLESWITCH switch} instructions. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} is a branch instruction with a single operand - */ - public static boolean isBranch(int opcode) { - return (flagsArray[opcode & 0xff] & BRANCH) != 0; - } - - /** - * Determines if a given opcode denotes a conditional branch. - * @param opcode - * @return {@code true} iff {@code opcode} is a conditional branch - */ - public static boolean isConditionalBranch(int opcode) { - return (flagsArray[opcode & 0xff] & FALL_THROUGH) != 0; - } - - /** - * Determines if a given opcode denotes a standard bytecode. A standard bytecode is - * defined in the JVM specification. - * - * @param opcode an opcode to test - * @return {@code true} iff {@code opcode} is a standard bytecode - */ - public static boolean isStandard(int opcode) { - return (flagsArray[opcode & 0xff] & EXTENSION) == 0; - } - - /** - * Determines if a given opcode denotes an extended bytecode. - * - * @param opcode an opcode to test - * @return {@code true} if {@code opcode} is an extended bytecode - */ - public static boolean isExtended(int opcode) { - return (flagsArray[opcode & 0xff] & EXTENSION) != 0; - } - - /** - * Determines if a given opcode is a three-byte extended bytecode. - * - * @param opcode an opcode to test - * @return {@code true} if {@code (opcode & ~0xff) != 0} - */ - public static boolean isThreeByteExtended(int opcode) { - return (opcode & ~0xff) != 0; - } - - /** - * Gets the arithmetic operator name for a given opcode. If {@code opcode} does not denote an - * arithmetic instruction, then the {@linkplain #nameOf(int) name} of the opcode is returned - * instead. - * - * @param op an opcode - * @return the arithmetic operator name - */ - public static String operator(int op) { - // Checkstyle: stop - switch (op) { - // arithmetic ops - case IADD : // fall through - case LADD : // fall through - case FADD : // fall through - case DADD : return "+"; - case ISUB : // fall through - case LSUB : // fall through - case FSUB : // fall through - case DSUB : return "-"; - case IMUL : // fall through - case LMUL : // fall through - case FMUL : // fall through - case DMUL : return "*"; - case IDIV : // fall through - case LDIV : // fall through - case FDIV : // fall through - case DDIV : return "/"; - case IREM : // fall through - case LREM : // fall through - case FREM : // fall through - case DREM : return "%"; - // shift ops - case ISHL : // fall through - case LSHL : return "<<"; - case ISHR : // fall through - case LSHR : return ">>"; - case IUSHR: // fall through - case LUSHR: return ">>>"; - // logic ops - case IAND : // fall through - case LAND : return "&"; - case IOR : // fall through - case LOR : return "|"; - case IXOR : // fall through - case LXOR : return "^"; - } - // Checkstyle: resume - return nameOf(op); - } - - /** - * Defines a bytecode by entering it into the arrays that record its name, length and flags. - * - * @param name instruction name (should be lower case) - * @param format encodes the length of the instruction - * @param flagsArray the set of {@link Flags} associated with the instruction - */ - private static void def(int opcode, String name, String format) { - def(opcode, name, format, 0); - } - - /** - * Defines a bytecode by entering it into the arrays that record its name, length and flags. - * - * @param name instruction name (lower case) - * @param format encodes the length of the instruction - * @param flags the set of {@link Flags} associated with the instruction - */ - private static void def(int opcode, String name, String format, int flags) { - assert nameArray[opcode] == null : "opcode " + opcode + " is already bound to name " + nameArray[opcode]; - nameArray[opcode] = name; - int instructionLength = format.length(); - lengthArray[opcode] = instructionLength; - Bytecodes.flagsArray[opcode] = flags; - - assert !isConditionalBranch(opcode) || isBranch(opcode) : "a conditional branch must also be a branch"; - } - - /** - * Utility for ensuring that the extended opcodes are contiguous and follow on directly - * from the standard JVM opcodes. If these conditions do not hold for the input source - * file, then it is modified 'in situ' to fix the problem. - * - * @param args {@code args[0]} is the path to this source file - */ - public static void main(String[] args) throws Exception { - Method findWorkspaceDirectory = Class.forName("com.sun.max.ide.JavaProject").getDeclaredMethod("findWorkspaceDirectory"); - File base = new File((File) findWorkspaceDirectory.invoke(null), "com.oracle.max.cri/src"); - File file = new File(base, Bytecodes.class.getName().replace('.', File.separatorChar) + ".java").getAbsoluteFile(); - - Pattern opcodeDecl = Pattern.compile("(\\s*public static final int )(\\w+)(\\s*=\\s*)(\\d+)(;.*)"); - - BufferedReader br = new BufferedReader(new FileReader(file)); - CharArrayWriter buffer = new CharArrayWriter((int) file.length()); - PrintWriter out = new PrintWriter(buffer); - String line; - int lastExtendedOpcode = BREAKPOINT; - boolean modified = false; - int section = 0; - while ((line = br.readLine()) != null) { - if (section == 0) { - if (line.equals(" // Start extended bytecodes")) { - section = 1; - } - } else if (section == 1) { - if (line.equals(" // End extended bytecodes")) { - section = 2; - } else { - Matcher matcher = opcodeDecl.matcher(line); - if (matcher.matches()) { - String name = matcher.group(2); - String value = matcher.group(4); - int opcode = Integer.parseInt(value); - if (nameArray[opcode] == null || !nameArray[opcode].equalsIgnoreCase(name)) { - throw new RuntimeException("Missing definition of name and flags for " + opcode + ":" + name + " -- " + nameArray[opcode]); - } - if (opcode != lastExtendedOpcode + 1) { - System.err.println("Fixed declaration of opcode " + name + " to be " + (lastExtendedOpcode + 1) + " (was " + value + ")"); - opcode = lastExtendedOpcode + 1; - line = line.substring(0, matcher.start(4)) + opcode + line.substring(matcher.end(4)); - modified = true; - } - - if (opcode >= 256) { - throw new RuntimeException("Exceeded maximum opcode value with " + name); - } - - lastExtendedOpcode = opcode; - } - } - } - - out.println(line); - } - if (section == 0) { - throw new RuntimeException("Did not find line starting extended bytecode declarations:\n\n // Start extended bytecodes"); - } else if (section == 1) { - throw new RuntimeException("Did not find line ending extended bytecode declarations:\n\n // End extended bytecodes"); - } - - if (modified) { - out.flush(); - FileWriter fileWriter = new FileWriter(file); - fileWriter.write(buffer.toCharArray()); - fileWriter.close(); - - System.out.println("Modified: " + file); - } - - - // Uncomment to print out visitor method declarations: -// for (int opcode = 0; opcode < flags.length; ++opcode) { -// if (isExtension(opcode)) { -// String visitorParams = length(opcode) == 1 ? "" : "int index"; -// System.out.println("@Override"); -// System.out.println("protected void " + name(opcode) + "(" + visitorParams + ") {"); -// System.out.println("}"); -// System.out.println(); -// } -// } - - // Uncomment to print out visitor method declarations: -// for (int opcode = 0; opcode < flags.length; ++opcode) { -// if (isExtension(opcode)) { -// System.out.println("case " + name(opcode).toUpperCase() + ": {"); -// String arg = ""; -// int length = length(opcode); -// if (length == 2) { -// arg = "readUnsigned1()"; -// } else if (length == 3) { -// arg = "readUnsigned2()"; -// } -// System.out.println(" bytecodeVisitor." + name(opcode) + "(" + arg + ");"); -// System.out.println(" break;"); -// System.out.println("}"); -// } -// } - - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/Bytes.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/Bytes.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -/** - * A collection of utility methods for dealing with bytes, particularly in byte arrays. - */ -public class Bytes { - /** - * Gets a signed 1-byte value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @return the signed 1-byte value at index {@code bci} in array {@code data} - */ - public static int beS1(byte[] data, int bci) { - return data[bci]; - } - - /** - * Gets a signed 2-byte big-endian value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @return the signed 2-byte, big-endian, value at index {@code bci} in array {@code data} - */ - public static int beS2(byte[] data, int bci) { - return (data[bci] << 8) | (data[bci + 1] & 0xff); - } - - /** - * Gets an unsigned 1-byte value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @return the unsigned 1-byte value at index {@code bci} in array {@code data} - */ - public static int beU1(byte[] data, int bci) { - return data[bci] & 0xff; - } - - /** - * Gets an unsigned 2-byte big-endian value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @return the unsigned 2-byte, big-endian, value at index {@code bci} in array {@code data} - */ - public static int beU2(byte[] data, int bci) { - return ((data[bci] & 0xff) << 8) | (data[bci + 1] & 0xff); - } - - /** - * Gets a signed 4-byte big-endian value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @return the signed 4-byte, big-endian, value at index {@code bci} in array {@code data} - */ - public static int beS4(byte[] data, int bci) { - return (data[bci] << 24) | ((data[bci + 1] & 0xff) << 16) | ((data[bci + 2] & 0xff) << 8) | (data[bci + 3] & 0xff); - } - - /** - * Gets either a signed 2-byte or a signed 4-byte big-endian value. - * @param data the array containing the data - * @param bci the start index of the value to retrieve - * @param fourByte if true, this method will return a 4-byte value - * @return the signed 2 or 4-byte, big-endian, value at index {@code bci} in array {@code data} - */ - public static int beSVar(byte[] data, int bci, boolean fourByte) { - if (fourByte) { - return beS4(data, bci); - } else { - return beS2(data, bci); - } - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/FrameStateBuilder.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/FrameStateBuilder.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,532 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import static com.oracle.max.graal.nodes.ValueUtil.*; -import static java.lang.reflect.Modifier.*; - -import java.util.*; - -import com.oracle.max.cri.ci.*; -import com.oracle.max.cri.ri.*; -import com.oracle.max.graal.nodes.*; -import com.oracle.max.graal.nodes.PhiNode.PhiType; -import com.oracle.max.graal.nodes.spi.*; -import com.oracle.max.graal.nodes.type.*; - - -public class FrameStateBuilder implements FrameStateAccess { - - private final StructuredGraph graph; - - private final ValueNode[] locals; - private final ValueNode[] stack; - private final ArrayList locks; - - private int stackIndex; - private boolean rethrowException; - - private final RiResolvedMethod method; - - public FrameStateBuilder(RiResolvedMethod method, int maxLocals, int maxStackSize, StructuredGraph graph) { - assert graph != null; - this.method = method; - this.graph = graph; - this.locals = new ValueNode[maxLocals]; - // we always need at least one stack slot (for exceptions) - int stackSize = Math.max(1, maxStackSize); - this.stack = new ValueNode[stackSize]; - - int javaIndex = 0; - int index = 0; - if (!isStatic(method.accessFlags())) { - // add the receiver - LocalNode local = graph.unique(new LocalNode(javaIndex, StampFactory.declaredNonNull(method.holder()))); - storeLocal(javaIndex, local); - javaIndex = 1; - index = 1; - } - RiSignature sig = method.signature(); - int max = sig.argumentCount(false); - RiType accessingClass = method.holder(); - for (int i = 0; i < max; i++) { - RiType type = sig.argumentTypeAt(i, accessingClass); - CiKind kind = type.kind(false).stackKind(); - Stamp stamp; - if (kind == CiKind.Object && type instanceof RiResolvedType) { - RiResolvedType resolvedType = (RiResolvedType) type; - stamp = StampFactory.declared(resolvedType); - } else { - stamp = StampFactory.forKind(kind); - } - LocalNode local = graph.unique(new LocalNode(index, stamp)); - storeLocal(javaIndex, local); - javaIndex += stackSlots(kind); - index++; - } - this.locks = new ArrayList<>(); - } - - @Override - public String toString() { - return String.format("FrameStateBuilder[stackSize=%d]", stackIndex); - } - - public void initializeFrom(FrameState other) { - assert locals.length == other.localsSize() : "expected: " + locals.length + ", actual: " + other.localsSize(); - assert stack.length >= other.stackSize() : "expected: <=" + stack.length + ", actual: " + other.stackSize(); - - this.stackIndex = other.stackSize(); - for (int i = 0; i < other.localsSize(); i++) { - locals[i] = other.localAt(i); - } - for (int i = 0; i < other.stackSize(); i++) { - stack[i] = other.stackAt(i); - } - locks.clear(); - for (int i = 0; i < other.locksSize(); i++) { - locks.add(other.lockAt(i)); - } - this.rethrowException = other.rethrowException(); - } - - public FrameState create(int bci) { - return graph.add(new FrameState(method, bci, locals, stack, stackIndex, locks, rethrowException)); - } - - public FrameState duplicateWithException(int bci, ValueNode exceptionObject) { - FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, locks, true)); - frameState.setOuterFrameState(outerFrameState()); - return frameState; - } - - /** - * Pushes an instruction onto the stack with the expected type. - * @param kind the type expected for this instruction - * @param x the instruction to push onto the stack - */ - public void push(CiKind kind, ValueNode x) { - assert kind != CiKind.Void; - xpush(assertKind(kind, x)); - if (isTwoSlot(kind)) { - xpush(null); - } - } - - /** - * Pushes a value onto the stack without checking the type. - * @param x the instruction to push onto the stack - */ - public void xpush(ValueNode x) { - assert x == null || !x.isDeleted(); - assert x == null || (x.kind() != CiKind.Void && x.kind() != CiKind.Illegal) : "unexpected value: " + x; - stack[stackIndex++] = x; - } - - /** - * Pushes a value onto the stack and checks that it is an int. - * @param x the instruction to push onto the stack - */ - public void ipush(ValueNode x) { - xpush(assertInt(x)); - } - - /** - * Pushes a value onto the stack and checks that it is a float. - * @param x the instruction to push onto the stack - */ - public void fpush(ValueNode x) { - xpush(assertFloat(x)); - } - - /** - * Pushes a value onto the stack and checks that it is an object. - * @param x the instruction to push onto the stack - */ - public void apush(ValueNode x) { - xpush(assertObject(x)); - } - - /** - * Pushes a value onto the stack and checks that it is a JSR return address. - * @param x the instruction to push onto the stack - */ - public void jpush(ValueNode x) { - xpush(assertJsr(x)); - } - - /** - * Pushes a value onto the stack and checks that it is a long. - * - * @param x the instruction to push onto the stack - */ - public void lpush(ValueNode x) { - xpush(assertLong(x)); - xpush(null); - } - - /** - * Pushes a value onto the stack and checks that it is a double. - * @param x the instruction to push onto the stack - */ - public void dpush(ValueNode x) { - xpush(assertDouble(x)); - xpush(null); - } - - public void pushReturn(CiKind kind, ValueNode x) { - if (kind != CiKind.Void) { - push(kind.stackKind(), x); - } - } - - /** - * Pops an instruction off the stack with the expected type. - * @param kind the expected type - * @return the instruction on the top of the stack - */ - public ValueNode pop(CiKind kind) { - assert kind != CiKind.Void; - if (isTwoSlot(kind)) { - xpop(); - } - return assertKind(kind, xpop()); - } - - /** - * Pops a value off of the stack without checking the type. - * @return x the instruction popped off the stack - */ - public ValueNode xpop() { - ValueNode result = stack[--stackIndex]; - assert result == null || !result.isDeleted(); - return result; - } - - /** - * Pops a value off of the stack and checks that it is an int. - * @return x the instruction popped off the stack - */ - public ValueNode ipop() { - return assertInt(xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a float. - * @return x the instruction popped off the stack - */ - public ValueNode fpop() { - return assertFloat(xpop()); - } - - /** - * Pops a value off of the stack and checks that it is an object. - * @return x the instruction popped off the stack - */ - public ValueNode apop() { - return assertObject(xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a JSR return address. - * @return x the instruction popped off the stack - */ - public ValueNode jpop() { - return assertJsr(xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a long. - * @return x the instruction popped off the stack - */ - public ValueNode lpop() { - assertHigh(xpop()); - return assertLong(xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a double. - * @return x the instruction popped off the stack - */ - public ValueNode dpop() { - assertHigh(xpop()); - return assertDouble(xpop()); - } - - /** - * Pop the specified number of slots off of this stack and return them as an array of instructions. - * @param size the number of arguments off of the stack - * @return an array containing the arguments off of the stack - */ - public ValueNode[] popArguments(int slotSize, int argSize) { - int base = stackIndex - slotSize; - ValueNode[] r = new ValueNode[argSize]; - int argIndex = 0; - int stackindex = 0; - while (stackindex < slotSize) { - ValueNode element = stack[base + stackindex]; - assert element != null; - r[argIndex++] = element; - stackindex += stackSlots(element.kind()); - } - stackIndex = base; - return r; - } - - /** - * Peeks an element from the operand stack. - * @param argumentNumber The number of the argument, relative from the top of the stack (0 = top). - * Long and double arguments only count as one argument, i.e., null-slots are ignored. - * @return The peeked argument. - */ - public ValueNode peek(int argumentNumber) { - int idx = stackSize() - 1; - for (int i = 0; i < argumentNumber; i++) { - if (stackAt(idx) == null) { - idx--; - assert isTwoSlot(stackAt(idx).kind()); - } - idx--; - } - return stackAt(idx); - } - - /** - * Truncates this stack to the specified size. - * @param size the size to truncate to - */ - public void truncateStack(int size) { - stackIndex = size; - assert stackIndex >= 0; - } - - /** - * Clears all values on this stack. - */ - public void clearStack() { - stackIndex = 0; - } - - /** - * Loads the local variable at the specified index. - * - * @param i the index of the local variable to load - * @return the instruction that produced the specified local - */ - public ValueNode loadLocal(int i) { - ValueNode x = locals[i]; - if (x != null) { - if (x instanceof PhiNode) { - assert ((PhiNode) x).type() == PhiType.Value; - if (x.isDeleted()) { - return null; - } - } - assert !isTwoSlot(x.kind()) || locals[i + 1] == null || locals[i + 1] instanceof PhiNode; - } - return x; - } - - /** - * Stores a given local variable at the specified index. If the value is a {@linkplain CiKind#isDoubleWord() double word}, - * then the next local variable index is also overwritten. - * - * @param i the index at which to store - * @param x the instruction which produces the value for the local - */ - public void storeLocal(int i, ValueNode x) { - assert x == null || (x.kind() != CiKind.Void && x.kind() != CiKind.Illegal) : "unexpected value: " + x; - locals[i] = x; - if (isTwoSlot(x.kind())) { - // (tw) if this was a double word then kill i+1 - locals[i + 1] = null; - } - if (i > 0) { - // if there was a double word at i - 1, then kill it - ValueNode p = locals[i - 1]; - if (p != null && isTwoSlot(p.kind())) { - locals[i - 1] = null; - } - } - } - - /** - * Locks a new object within the specified IRScope. - * @param scope the IRScope in which this locking operation occurs - * @param obj the object being locked - */ - public void lock(MonitorObject obj) { - assert obj == null || (obj.kind() != CiKind.Void && obj.kind() != CiKind.Illegal) : "unexpected value: " + obj; - locks.add(obj); - } - - /** - * Unlock the lock on the top of the stack. - */ - public void unlock(MonitorObject obj) { - assert locks.get(locks.size() - 1) == obj; - locks.remove(locks.size() - 1); - } - - /** - * Get the value on the stack at the specified stack index. - * - * @param i the index into the stack, with {@code 0} being the bottom of the stack - * @return the instruction at the specified position in the stack - */ - public final ValueNode stackAt(int i) { - return stack[i]; - } - - /** - * Gets the value in the local variables at the specified index. - * - * @param i the index into the locals - * @return the instruction that produced the value for the specified local - */ - public final ValueNode localAt(int i) { - return locals[i]; - } - - /** - * Retrieves the lock at the specified index in the lock stack. - * @param i the index into the lock stack - * @return the instruction which produced the object at the specified location in the lock stack - */ - public final MonitorObject lockAt(int i) { - return locks.get(i); - } - - /** - * Returns the size of the local variables. - * - * @return the size of the local variables - */ - public int localsSize() { - return locals.length; - } - - /** - * Gets number of locks held by this frame state. - */ - public int locksSize() { - return locks.size(); - } - - /** - * Gets the current size (height) of the stack. - */ - public int stackSize() { - return stackIndex; - } - - public Iterator locals() { - return new ValueArrayIterator(locals); - } - - public Iterator stack() { - return new ValueArrayIterator(locals); - } - - public List locks() { - return Collections.unmodifiableList(locks); - } - - - private static class ValueArrayIterator implements Iterator { - private final ValueNode[] array; - private int index; - - public ValueArrayIterator(ValueNode[] array, int length) { - assert length <= array.length; - this.array = array; - this.index = 0; - } - - public ValueArrayIterator(ValueNode[] array) { - this(array, array.length); - } - - @Override - public boolean hasNext() { - return index < array.length; - } - - @Override - public ValueNode next() { - return array[index++]; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("cannot remove from array"); - } - - } - - - @Override - public FrameState duplicate(int bci) { - return create(bci); - } - - @Override - public ValueNode valueAt(int i) { - if (i < locals.length) { - return locals[i]; - } else if (i < locals.length + stackIndex) { - return stack[i - locals.length]; - } else { - return locks.get(i - locals.length - stack.length); - } - } - - @Override - public FrameState outerFrameState() { - return null; - } - - public FrameState duplicateWithoutStack(int bci) { - FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, locks, false)); - frameState.setOuterFrameState(outerFrameState()); - return frameState; - } - - @Override - public boolean rethrowException() { - return rethrowException; - } - - public void setRethrowException(boolean b) { - rethrowException = b; - } - - public static int stackSlots(CiKind kind) { - return isTwoSlot(kind) ? 2 : 1; - } - - public static boolean isTwoSlot(CiKind kind) { - assert kind != CiKind.Void && kind != CiKind.Illegal; - return kind == CiKind.Long || kind == CiKind.Double; - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/GraphBuilderConfiguration.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/GraphBuilderConfiguration.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.phases.*; - -public class GraphBuilderConfiguration { - private final boolean useBranchPrediction; - private final boolean eagerResolving; - private final PhasePlan plan; - - public GraphBuilderConfiguration(boolean useBranchPrediction, boolean eagerResolving, PhasePlan plan) { - this.useBranchPrediction = useBranchPrediction; - this.eagerResolving = eagerResolving; - this.plan = plan; - } - - public boolean useBranchPrediction() { - return useBranchPrediction; - } - - public boolean eagerResolving() { - return eagerResolving; - } - - public PhasePlan plan() { - return plan; - } - - public static GraphBuilderConfiguration getDefault() { - return getDefault(null); - } - - public static GraphBuilderConfiguration getDefault(PhasePlan plan) { - return new GraphBuilderConfiguration(GraalOptions.UseBranchPrediction, false, plan); - } - - public static GraphBuilderConfiguration getDeoptFreeDefault() { - return getDeoptFreeDefault(null); - } - - public static GraphBuilderConfiguration getDeoptFreeDefault(PhasePlan plan) { - return new GraphBuilderConfiguration(false, true, plan); - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/GraphBuilderPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/GraphBuilderPhase.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1749 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import static com.oracle.max.graal.compiler.graphbuilder.Bytecodes.*; -import static java.lang.reflect.Modifier.*; - -import java.lang.reflect.*; -import java.util.*; - -import com.oracle.max.cri.ci.*; -import com.oracle.max.cri.ri.*; -import com.oracle.max.cri.ri.RiType.*; -import com.oracle.max.criutils.*; -import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.BlockMap.Block; -import com.oracle.max.graal.compiler.graphbuilder.BlockMap.DeoptBlock; -import com.oracle.max.graal.compiler.graphbuilder.BlockMap.ExceptionBlock; -import com.oracle.max.graal.compiler.phases.*; -import com.oracle.max.graal.compiler.schedule.*; -import com.oracle.max.graal.compiler.util.*; -import com.oracle.max.graal.graph.*; -import com.oracle.max.graal.nodes.*; -import com.oracle.max.graal.nodes.DeoptimizeNode.DeoptAction; -import com.oracle.max.graal.nodes.PhiNode.PhiType; -import com.oracle.max.graal.nodes.calc.*; -import com.oracle.max.graal.nodes.extended.*; -import com.oracle.max.graal.nodes.java.*; -import com.oracle.max.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.max.graal.nodes.spi.*; - -/** - * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph. - */ -public final class GraphBuilderPhase extends Phase { - - /** - * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace - * the bytecode instructions as they are parsed. - */ - public static final int TRACELEVEL_INSTRUCTIONS = 1; - - /** - * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace - * the frame state before each bytecode instruction as it is parsed. - */ - public static final int TRACELEVEL_STATE = 2; - - private StructuredGraph currentGraph; - - private final CiStatistics stats; - private final RiRuntime runtime; - private RiConstantPool constantPool; - private RiExceptionHandler[] exceptionHandlers; - private RiResolvedMethod method; - - private BytecodeStream stream; // the bytecode stream - private final LogStream log; - - private FrameStateBuilder frameState; // the current execution state - private Block currentBlock; - - private int nextBlockNumber; - - private ValueNode methodSynchronizedObject; - private ExceptionBlock unwindBlock; - private Block returnBlock; - - // the worklist of blocks, sorted by depth first number - private final PriorityQueue workList = new PriorityQueue<>(10, new Comparator() { - public int compare(Block o1, Block o2) { - return o1.blockID - o2.blockID; - } - }); - - private FixedWithNextNode lastInstr; // the last instruction added - - private Set blocksOnWorklist; - private Set blocksVisited; - - private BitSet canTrapBitSet; - - public static final Map cachedGraphs = new WeakHashMap<>(); - - private final GraphBuilderConfiguration config; - - public GraphBuilderPhase(RiRuntime runtime) { - this(runtime, null); - } - - public GraphBuilderPhase(RiRuntime runtime, CiStatistics stats) { - this(runtime, stats, GraphBuilderConfiguration.getDefault()); - } - - public GraphBuilderPhase(RiRuntime runtime, CiStatistics stats, GraphBuilderConfiguration config) { - this.config = config; - this.runtime = runtime; - this.stats = stats; - this.log = GraalOptions.TraceBytecodeParserLevel > 0 ? new LogStream(TTY.out()) : null; - } - - @Override - protected void run(StructuredGraph graph) { - method = graph.method(); - assert method.code() != null : "method must contain bytecodes: " + method; - this.stream = new BytecodeStream(method.code()); - this.constantPool = method.getConstantPool(); - this.blocksOnWorklist = new HashSet<>(); - this.blocksVisited = new HashSet<>(); - unwindBlock = null; - returnBlock = null; - methodSynchronizedObject = null; - exceptionHandlers = null; - assert graph != null; - this.currentGraph = graph; - this.frameState = new FrameStateBuilder(method, method.maxLocals(), method.maxStackSize(), graph); - build(); - } - - @Override - protected String getDetailedName() { - return getName() + " " + CiUtil.format("%H.%n(%p):%r", method); - } - - private BlockMap createBlockMap() { - BlockMap map = new BlockMap(method, config.useBranchPrediction()); - map.build(); - if (stats != null) { - stats.bytecodeCount += method.code().length; - } - - if (currentContext.isObserved()) { - String label = CiUtil.format("BlockListBuilder %f %R %H.%n(%P)", method); - currentContext.observable.fireCompilationEvent(label, map); - } - return map; - } - - private void build() { - if (log != null) { - log.println(); - log.println("Compiling " + method); - } - - // compute the block map, setup exception handlers and get the entrypoint(s) - BlockMap blockMap = createBlockMap(); - this.canTrapBitSet = blockMap.canTrap; - - exceptionHandlers = blockMap.exceptionHandlers(); - if (stats != null) { - stats.blockCount += blockMap.blocks.size(); - } - nextBlockNumber = blockMap.blocks.size(); - - lastInstr = currentGraph.start(); - if (isSynchronized(method.accessFlags())) { - // add a monitor enter to the start block - currentGraph.start().setStateAfter(frameState.create(FrameState.BEFORE_BCI)); - methodSynchronizedObject = synchronizedObject(frameState, method); - lastInstr = genMonitorEnter(methodSynchronizedObject); - } - - // finish the start block - ((AbstractStateSplit) lastInstr).setStateAfter(frameState.create(0)); - if (blockMap.startBlock.isLoopHeader) { - appendGoto(createTarget(blockMap.startBlock, frameState)); - } else { - blockMap.startBlock.firstInstruction = lastInstr; - } - addToWorkList(blockMap.startBlock); - - iterateAllBlocks(); - connectLoopEndToBegin(); - - // remove Placeholders (except for loop exits) - for (PlaceholderNode n : currentGraph.getNodes(PlaceholderNode.class)) { - n.replaceAndDelete(n.next()); - } - - // remove dead FrameStates - for (Node n : currentGraph.getNodes(FrameState.class)) { - if (n.usages().size() == 0 && n.predecessor() == null) { - n.safeDelete(); - } - } - - if (GraalOptions.CacheGraphs && !currentGraph.hasNode(DeoptimizeNode.class)) { - cachedGraphs.put(method, currentGraph.copy()); - } - } - - private int nextBlockNumber() { - if (stats != null) { - stats.blockCount++; - } - return nextBlockNumber++; - } - - private Block unwindBlock(int bci) { - if (unwindBlock == null) { - unwindBlock = new ExceptionBlock(); - unwindBlock.startBci = -1; - unwindBlock.endBci = -1; - unwindBlock.deoptBci = bci; - unwindBlock.blockID = nextBlockNumber(); - addToWorkList(unwindBlock); - } - return unwindBlock; - } - - private Block returnBlock(int bci) { - if (returnBlock == null) { - returnBlock = new Block(); - returnBlock.startBci = bci; - returnBlock.endBci = bci; - returnBlock.blockID = nextBlockNumber(); - addToWorkList(returnBlock); - } - return returnBlock; - } - - private void markOnWorkList(Block block) { - blocksOnWorklist.add(block); - } - - private boolean isOnWorkList(Block block) { - return blocksOnWorklist.contains(block); - } - - private void markVisited(Block block) { - blocksVisited.add(block); - } - - private boolean isVisited(Block block) { - return blocksVisited.contains(block); - } - - public void mergeOrClone(Block target, FrameStateAccess newState) { - AbstractStateSplit first = (AbstractStateSplit) target.firstInstruction; - - if (target.isLoopHeader && isVisited(target)) { - first = (AbstractStateSplit) loopBegin(target).loopEnd().predecessor(); - } - - int bci = target.startBci; - if (target instanceof ExceptionBlock) { - bci = ((ExceptionBlock) target).deoptBci; - } - - FrameState existingState = first.stateAfter(); - - if (existingState == null) { - // copy state because it is modified - first.setStateAfter(newState.duplicate(bci)); - } else { - if (!GraalOptions.AssumeVerifiedBytecode && !existingState.isCompatibleWith(newState)) { - // stacks or locks do not match--bytecodes would not verify - TTY.println(existingState.toString()); - TTY.println(newState.duplicate(0).toString()); - throw new CiBailout("stack or locks do not match"); - } - assert existingState.localsSize() == newState.localsSize(); - assert existingState.stackSize() == newState.stackSize(); - - if (first instanceof PlaceholderNode) { - PlaceholderNode p = (PlaceholderNode) first; - if (p.predecessor() == null) { - p.setStateAfter(newState.duplicate(bci)); - return; - } else { - MergeNode merge = currentGraph.add(new MergeNode()); - FixedNode next = p.next(); - EndNode end = currentGraph.add(new EndNode()); - p.setNext(end); - merge.setNext(next); - merge.addEnd(end); - merge.setStateAfter(existingState); - p.setStateAfter(existingState.duplicate(bci)); - if (!(next instanceof LoopEndNode)) { - target.firstInstruction = merge; - } - first = merge; - } - } - - existingState.merge((MergeNode) first, newState); - } - } - - public BytecodeStream stream() { - return stream; - } - - public int bci() { - return stream.currentBCI(); - } - - private void loadLocal(int index, CiKind kind) { - frameState.push(kind, frameState.loadLocal(index)); - } - - private void storeLocal(CiKind kind, int index) { - frameState.storeLocal(index, frameState.pop(kind)); - } - - public static boolean covers(RiExceptionHandler handler, int bci) { - return handler.startBCI() <= bci && bci < handler.endBCI(); - } - - public static boolean isCatchAll(RiExceptionHandler handler) { - return handler.catchTypeCPI() == 0; - } - - private BeginNode handleException(ValueNode exceptionObject, int bci) { - assert bci == FrameState.BEFORE_BCI || bci == bci() : "invalid bci"; - - if (GraalOptions.UseExceptionProbability && method.invocationCount() > GraalOptions.MatureInvocationCount) { - if (bci != FrameState.BEFORE_BCI && exceptionObject == null && method.exceptionProbability(bci) == 0) { - return null; - } - } - - 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 = 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; - } - } - } - - BeginNode p = currentGraph.add(new BeginNode()); - p.setStateAfter(frameState.duplicateWithoutStack(bci)); - - ValueNode currentExceptionObject; - ExceptionObjectNode newObj = null; - if (exceptionObject == null) { - newObj = currentGraph.add(new ExceptionObjectNode()); - currentExceptionObject = newObj; - } else { - currentExceptionObject = exceptionObject; - } - FrameState stateWithException = frameState.duplicateWithException(bci, currentExceptionObject); - if (newObj != null) { - newObj.setStateAfter(stateWithException); - } - FixedNode target = createTarget(dispatchBlock, stateWithException); - if (exceptionObject == null) { - ExceptionObjectNode eObj = (ExceptionObjectNode) currentExceptionObject; - eObj.setNext(target); - p.setNext(eObj); - } else { - p.setNext(target); - } - return p; - } - - private void genLoadConstant(int cpi) { - Object con = constantPool.lookupConstant(cpi); - - if (con instanceof RiType) { - // this is a load of class constant which might be unresolved - RiType riType = (RiType) con; - if (riType instanceof RiResolvedType) { - frameState.push(CiKind.Object, append(ConstantNode.forCiConstant(((RiResolvedType) riType).getEncoding(Representation.JavaClass), runtime, currentGraph))); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.push(CiKind.Object, append(ConstantNode.forObject(null, runtime, currentGraph))); - } - } else if (con instanceof CiConstant) { - CiConstant constant = (CiConstant) con; - frameState.push(constant.kind.stackKind(), appendConstant(constant)); - } else { - throw new Error("lookupConstant returned an object of incorrect type"); - } - } - - private void genLoadIndexed(CiKind kind) { - emitExplicitExceptions(frameState.peek(1), frameState.peek(0)); - - ValueNode index = frameState.ipop(); - ValueNode array = frameState.apop(); - ValueNode length = append(currentGraph.add(new ArrayLengthNode(array))); - ValueNode v = append(currentGraph.add(new LoadIndexedNode(array, index, length, kind))); - frameState.push(kind.stackKind(), v); - } - - private void genStoreIndexed(CiKind kind) { - emitExplicitExceptions(frameState.peek(2), frameState.peek(1)); - - ValueNode value = frameState.pop(kind.stackKind()); - ValueNode index = frameState.ipop(); - ValueNode array = frameState.apop(); - ValueNode length = append(currentGraph.add(new ArrayLengthNode(array))); - StoreIndexedNode result = currentGraph.add(new StoreIndexedNode(array, index, length, kind, value)); - append(result); - } - - private void stackOp(int opcode) { - switch (opcode) { - case POP: { - frameState.xpop(); - break; - } - case POP2: { - frameState.xpop(); - frameState.xpop(); - break; - } - case DUP: { - ValueNode w = frameState.xpop(); - frameState.xpush(w); - frameState.xpush(w); - break; - } - case DUP_X1: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - frameState.xpush(w1); - frameState.xpush(w2); - frameState.xpush(w1); - break; - } - case DUP_X2: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - ValueNode w3 = frameState.xpop(); - frameState.xpush(w1); - frameState.xpush(w3); - frameState.xpush(w2); - frameState.xpush(w1); - break; - } - case DUP2: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - frameState.xpush(w2); - frameState.xpush(w1); - frameState.xpush(w2); - frameState.xpush(w1); - break; - } - case DUP2_X1: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - ValueNode w3 = frameState.xpop(); - frameState.xpush(w2); - frameState.xpush(w1); - frameState.xpush(w3); - frameState.xpush(w2); - frameState.xpush(w1); - break; - } - case DUP2_X2: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - ValueNode w3 = frameState.xpop(); - ValueNode w4 = frameState.xpop(); - frameState.xpush(w2); - frameState.xpush(w1); - frameState.xpush(w4); - frameState.xpush(w3); - frameState.xpush(w2); - frameState.xpush(w1); - break; - } - case SWAP: { - ValueNode w1 = frameState.xpop(); - ValueNode w2 = frameState.xpop(); - frameState.xpush(w1); - frameState.xpush(w2); - break; - } - default: - throw Util.shouldNotReachHere(); - } - - } - - private void genArithmeticOp(CiKind result, int opcode, boolean canTrap) { - ValueNode y = frameState.pop(result); - ValueNode x = frameState.pop(result); - boolean isStrictFP = isStrict(method.accessFlags()); - ArithmeticNode v; - switch(opcode){ - case IADD: - case LADD: v = new IntegerAddNode(result, x, y); break; - case FADD: - case DADD: v = new FloatAddNode(result, x, y, isStrictFP); break; - case ISUB: - case LSUB: v = new IntegerSubNode(result, x, y); break; - case FSUB: - case DSUB: v = new FloatSubNode(result, x, y, isStrictFP); break; - case IMUL: - case LMUL: v = new IntegerMulNode(result, x, y); break; - case FMUL: - case DMUL: v = new FloatMulNode(result, x, y, isStrictFP); break; - case IDIV: - case LDIV: v = new IntegerDivNode(result, x, y); break; - case FDIV: - case DDIV: v = new FloatDivNode(result, x, y, isStrictFP); break; - case IREM: - case LREM: v = new IntegerRemNode(result, x, y); break; - case FREM: - case DREM: v = new FloatRemNode(result, x, y, isStrictFP); break; - default: - throw new CiBailout("should not reach"); - } - ValueNode result1 = append(currentGraph.unique(v)); - if (canTrap) { - append(currentGraph.add(new ValueAnchorNode(result1))); - } - frameState.push(result, result1); - } - - private void genNegateOp(CiKind kind) { - frameState.push(kind, append(currentGraph.unique(new NegateNode(frameState.pop(kind))))); - } - - private void genShiftOp(CiKind kind, int opcode) { - ValueNode s = frameState.ipop(); - ValueNode x = frameState.pop(kind); - ShiftNode v; - switch(opcode){ - case ISHL: - case LSHL: v = new LeftShiftNode(kind, x, s); break; - case ISHR: - case LSHR: v = new RightShiftNode(kind, x, s); break; - case IUSHR: - case LUSHR: v = new UnsignedRightShiftNode(kind, x, s); break; - default: - throw new CiBailout("should not reach"); - } - frameState.push(kind, append(currentGraph.unique(v))); - } - - private void genLogicOp(CiKind kind, int opcode) { - ValueNode y = frameState.pop(kind); - ValueNode x = frameState.pop(kind); - LogicNode v; - switch(opcode){ - case IAND: - case LAND: v = new AndNode(kind, x, y); break; - case IOR: - case LOR: v = new OrNode(kind, x, y); break; - case IXOR: - case LXOR: v = new XorNode(kind, x, y); break; - default: - throw new CiBailout("should not reach"); - } - frameState.push(kind, append(currentGraph.unique(v))); - } - - private void genCompareOp(CiKind kind, boolean isUnorderedLess) { - ValueNode y = frameState.pop(kind); - ValueNode x = frameState.pop(kind); - frameState.ipush(append(currentGraph.unique(new NormalizeCompareNode(x, y, isUnorderedLess)))); - } - - private void genConvert(ConvertNode.Op opcode) { - ValueNode input = frameState.pop(opcode.from.stackKind()); - frameState.push(opcode.to.stackKind(), append(currentGraph.unique(new ConvertNode(opcode, input)))); - } - - private void genIncrement() { - int index = stream().readLocalIndex(); - int delta = stream().readIncrement(); - ValueNode x = frameState.localAt(index); - ValueNode y = append(ConstantNode.forInt(delta, currentGraph)); - frameState.storeLocal(index, append(currentGraph.unique(new IntegerAddNode(CiKind.Int, x, y)))); - } - - private void genGoto() { - appendGoto(createTarget(currentBlock.successors.get(0), frameState)); - assert currentBlock.normalSuccessors == 1; - } - - private void ifNode(ValueNode x, Condition cond, ValueNode y) { - assert !x.isDeleted() && !y.isDeleted(); - double probability = method.branchProbability(bci()); - if (probability < 0) { - if (GraalOptions.TraceProbability) { - TTY.println("missing probability in " + method + " at bci " + bci()); - } - probability = 0.5; - } - - IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(x, cond, y)), probability)); - append(ifNode); - ifNode.setTrueSuccessor(BeginNode.begin(createTarget(currentBlock.successors.get(0), frameState))); - ifNode.setFalseSuccessor(BeginNode.begin(createTarget(currentBlock.successors.get(1), frameState))); - - assert currentBlock.normalSuccessors == 2 : currentBlock.normalSuccessors; - } - - private void genIfZero(Condition cond) { - ValueNode y = appendConstant(CiConstant.INT_0); - ValueNode x = frameState.ipop(); - ifNode(x, cond, y); - } - - private void genIfNull(Condition cond) { - ValueNode y = appendConstant(CiConstant.NULL_OBJECT); - ValueNode x = frameState.apop(); - ifNode(x, cond, y); - } - - private void genIfSame(CiKind kind, Condition cond) { - ValueNode y = frameState.pop(kind); - ValueNode x = frameState.pop(kind); - assert !x.isDeleted() && !y.isDeleted(); - ifNode(x, cond, y); - } - - private void genThrow(int bci) { - ValueNode exception = frameState.apop(); - FixedGuardNode node = currentGraph.add(new FixedGuardNode(currentGraph.unique(new NullCheckNode(exception, false)))); - append(node); - append(handleException(exception, bci)); - } - - private RiType lookupType(int cpi, int bytecode) { - eagerResolving(cpi, bytecode); - RiType result = constantPool.lookupType(cpi, bytecode); - assert !config.eagerResolving() || result instanceof RiResolvedType; - return result; - } - - private RiMethod lookupMethod(int cpi, int opcode) { - eagerResolving(cpi, opcode); - RiMethod result = constantPool.lookupMethod(cpi, opcode); - assert !config.eagerResolving() || ((result instanceof RiResolvedMethod) && ((RiResolvedMethod) result).holder().isInitialized()); - return result; - } - - private RiField lookupField(int cpi, int opcode) { - eagerResolving(cpi, opcode); - RiField result = constantPool.lookupField(cpi, opcode); - assert !config.eagerResolving() || (result instanceof RiResolvedField && ((RiResolvedField) result).holder().isInitialized()); - return result; - } - - private void eagerResolving(int cpi, int bytecode) { - if (config.eagerResolving()) { - constantPool.loadReferencedType(cpi, bytecode); - } - } - - private void genCheckCast() { - int cpi = stream().readCPI(); - RiType type = lookupType(cpi, CHECKCAST); - boolean initialized = type instanceof RiResolvedType; - if (initialized) { - ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, true); - ValueNode object = frameState.apop(); - AnchorNode anchor = currentGraph.add(new AnchorNode()); - append(anchor); - CheckCastNode checkCast = currentGraph.unique(new CheckCastNode(anchor, typeInstruction, (RiResolvedType) type, object)); - append(currentGraph.add(new ValueAnchorNode(checkCast))); - frameState.apush(checkCast); - } else { - ValueNode object = frameState.apop(); - append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new CompareNode(object, Condition.EQ, ConstantNode.forObject(null, runtime, currentGraph)))))); - frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); - } - } - - private void genInstanceOf() { - int cpi = stream().readCPI(); - RiType type = lookupType(cpi, INSTANCEOF); - ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, type instanceof RiResolvedType); - ValueNode object = frameState.apop(); - if (typeInstruction != null) { - frameState.ipush(append(MaterializeNode.create(currentGraph.unique(new InstanceOfNode(typeInstruction, (RiResolvedType) type, object, false)), currentGraph))); - } else { - frameState.ipush(appendConstant(CiConstant.INT_0)); - } - } - - void genNewInstance(int cpi) { - RiType type = lookupType(cpi, NEW); - if (type instanceof RiResolvedType) { - NewInstanceNode n = currentGraph.add(new NewInstanceNode((RiResolvedType) type)); - frameState.apush(append(n)); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); - } - } - - /** - * Gets the kind of array elements for the array type code that appears - * in a {@link Bytecodes#NEWARRAY} bytecode. - * @param code the array type code - * @return the kind from the array type code - */ - public static CiKind arrayTypeCodeToKind(int code) { - // Checkstyle: stop - switch (code) { - case 4: return CiKind.Boolean; - case 5: return CiKind.Char; - case 6: return CiKind.Float; - case 7: return CiKind.Double; - case 8: return CiKind.Byte; - case 9: return CiKind.Short; - case 10: return CiKind.Int; - case 11: return CiKind.Long; - default: throw new IllegalArgumentException("unknown array type code: " + code); - } - // Checkstyle: resume - } - - private void genNewTypeArray(int typeCode) { - CiKind kind = arrayTypeCodeToKind(typeCode); - RiResolvedType elementType = runtime.asRiType(kind); - NewTypeArrayNode nta = currentGraph.add(new NewTypeArrayNode(frameState.ipop(), elementType)); - frameState.apush(append(nta)); - } - - private void genNewObjectArray(int cpi) { - RiType type = lookupType(cpi, ANEWARRAY); - ValueNode length = frameState.ipop(); - if (type instanceof RiResolvedType) { - NewArrayNode n = currentGraph.add(new NewObjectArrayNode((RiResolvedType) type, length)); - frameState.apush(append(n)); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); - } - - } - - private void genNewMultiArray(int cpi) { - RiType type = lookupType(cpi, MULTIANEWARRAY); - int rank = stream().readUByte(bci() + 3); - ValueNode[] dims = new ValueNode[rank]; - for (int i = rank - 1; i >= 0; i--) { - dims[i] = frameState.ipop(); - } - if (type instanceof RiResolvedType) { - FixedWithNextNode n = currentGraph.add(new NewMultiArrayNode((RiResolvedType) type, dims)); - frameState.apush(append(n)); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); - } - } - - private void genGetField(RiField field) { - emitExplicitExceptions(frameState.peek(0), null); - - CiKind kind = field.kind(false); - ValueNode receiver = frameState.apop(); - if ((field instanceof RiResolvedField) && ((RiResolvedField) field).holder().isInitialized()) { - LoadFieldNode load = currentGraph.add(new LoadFieldNode(receiver, (RiResolvedField) field)); - appendOptimizedLoadField(kind, load); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); - } - } - - public static class ExceptionInfo { - - public final FixedWithNextNode exceptionEdge; - public final ValueNode exception; - - public ExceptionInfo(FixedWithNextNode exceptionEdge, ValueNode exception) { - this.exceptionEdge = exceptionEdge; - this.exception = exception; - } - } - - private ExceptionInfo emitNullCheck(ValueNode receiver) { - PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); - PlaceholderNode falseSucc = currentGraph.add(new PlaceholderNode()); - IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new NullCheckNode(receiver, false)), trueSucc, falseSucc, 1)); - - append(ifNode); - lastInstr = trueSucc; - - if (GraalOptions.OmitHotExceptionStacktrace) { - ValueNode exception = ConstantNode.forObject(new NullPointerException(), runtime, currentGraph); - return new ExceptionInfo(falseSucc, exception); - } else { - RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateNullPointerException)); - call.setStateAfter(frameState.duplicate(bci())); - falseSucc.setNext(call); - return new ExceptionInfo(call, call); - } - } - - private ExceptionInfo emitBoundsCheck(ValueNode index, ValueNode length) { - PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); - PlaceholderNode falseSucc = currentGraph.add(new PlaceholderNode()); - IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(index, Condition.BT, length)), trueSucc, falseSucc, 1)); - - append(ifNode); - lastInstr = trueSucc; - - if (GraalOptions.OmitHotExceptionStacktrace) { - ValueNode exception = ConstantNode.forObject(new ArrayIndexOutOfBoundsException(), runtime, currentGraph); - return new ExceptionInfo(falseSucc, exception); - } else { - RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateOutOfBoundsException, new ValueNode[] {index})); - call.setStateAfter(frameState.duplicate(bci())); - falseSucc.setNext(call); - return new ExceptionInfo(call, call); - } - } - - private void emitExplicitExceptions(ValueNode receiver, ValueNode outOfBoundsIndex) { - assert receiver != null; - - 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.addEnd(end); - phi.addInput(info.exception); - } - merge.setStateAfter(frameState.duplicate(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.duplicateWithException(bci(), exception.exception))); - } - if (GraalOptions.Meter) { - currentContext.metrics.ExplicitExceptions++; - } - } - } - - private void genPutField(RiField field) { - emitExplicitExceptions(frameState.peek(1), null); - - ValueNode value = frameState.pop(field.kind(false).stackKind()); - ValueNode receiver = frameState.apop(); - if (field instanceof RiResolvedField && ((RiResolvedField) field).holder().isInitialized()) { - StoreFieldNode store = currentGraph.add(new StoreFieldNode(receiver, (RiResolvedField) field, value)); - appendOptimizedStoreField(store); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - } - } - - private void genGetStatic(RiField field) { - RiType holder = field.holder(); - boolean isInitialized = (field instanceof RiResolvedField) && ((RiResolvedType) holder).isInitialized(); - CiConstant constantValue = null; - if (isInitialized) { - constantValue = ((RiResolvedField) field).constantValue(null); - } - if (constantValue != null) { - frameState.push(constantValue.kind.stackKind(), appendConstant(constantValue)); - } else { - ValueNode container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, isInitialized); - CiKind kind = field.kind(false); - if (container != null) { - LoadFieldNode load = currentGraph.add(new LoadFieldNode(container, (RiResolvedField) field)); - appendOptimizedLoadField(kind, load); - } else { - // deopt will be generated by genTypeOrDeopt, not needed here - frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); - } - } - } - - private void genPutStatic(RiField field) { - RiType holder = field.holder(); - ValueNode container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, field instanceof RiResolvedField && ((RiResolvedType) holder).isInitialized()); - ValueNode value = frameState.pop(field.kind(false).stackKind()); - if (container != null) { - StoreFieldNode store = currentGraph.add(new StoreFieldNode(container, (RiResolvedField) field, value)); - appendOptimizedStoreField(store); - } else { - // deopt will be generated by genTypeOrDeopt, not needed here - } - } - - private ConstantNode genTypeOrDeopt(RiType.Representation representation, RiType holder, boolean initialized) { - if (initialized) { - return appendConstant(((RiResolvedType) holder).getEncoding(representation)); - } else { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - return null; - } - } - - private void appendOptimizedStoreField(StoreFieldNode store) { - append(store); - } - - private void appendOptimizedLoadField(CiKind kind, LoadFieldNode load) { - // append the load to the instruction - ValueNode optimized = append(load); - frameState.push(kind.stackKind(), optimized); - } - - private void genInvokeStatic(RiMethod target) { - if (target instanceof RiResolvedMethod) { - RiResolvedMethod resolvedTarget = (RiResolvedMethod) target; - RiResolvedType holder = resolvedTarget.holder(); - if (!holder.isInitialized() && GraalOptions.ResolveClassBeforeStaticInvoke) { - genInvokeDeopt(target, false); - } else { - ValueNode[] args = frameState.popArguments(resolvedTarget.signature().argumentSlots(false), resolvedTarget.signature().argumentCount(false)); - appendInvoke(InvokeKind.Static, resolvedTarget, args); - } - } else { - genInvokeDeopt(target, false); - } - } - - private void genInvokeInterface(RiMethod target) { - if (target instanceof RiResolvedMethod) { - ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); - genInvokeIndirect(InvokeKind.Interface, (RiResolvedMethod) target, args); - } else { - genInvokeDeopt(target, true); - } - } - - private void genInvokeVirtual(RiMethod target) { - if (target instanceof RiResolvedMethod) { - ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); - genInvokeIndirect(InvokeKind.Virtual, (RiResolvedMethod) target, args); - } else { - genInvokeDeopt(target, true); - } - - } - - private void genInvokeSpecial(RiMethod target) { - if (target instanceof RiResolvedMethod) { - assert target != null; - assert target.signature() != null; - ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); - invokeDirect((RiResolvedMethod) target, args); - } else { - genInvokeDeopt(target, true); - } - } - - private void genInvokeDeopt(RiMethod unresolvedTarget, boolean withReceiver) { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); - frameState.popArguments(unresolvedTarget.signature().argumentSlots(withReceiver), unresolvedTarget.signature().argumentCount(withReceiver)); - CiKind kind = unresolvedTarget.signature().returnKind(false); - if (kind != CiKind.Void) { - frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); - } - } - - private void genInvokeIndirect(InvokeKind invokeKind, RiResolvedMethod target, ValueNode[] args) { - ValueNode receiver = args[0]; - // attempt to devirtualize the call - RiResolvedType klass = target.holder(); - - // 0. check for trivial cases - if (target.canBeStaticallyBound() && !isAbstract(target.accessFlags())) { - // check for trivial cases (e.g. final methods, nonvirtual methods) - invokeDirect(target, args); - return; - } - // 1. check if the exact type of the receiver can be determined - RiResolvedType exact = getExactType(klass, receiver); - if (exact != null) { - // either the holder class is exact, or the receiver object has an exact type - invokeDirect(exact.resolveMethodImpl(target), args); - return; - } - // devirtualization failed, produce an actual invokevirtual - appendInvoke(invokeKind, target, args); - } - - private void invokeDirect(RiResolvedMethod target, ValueNode[] args) { - appendInvoke(InvokeKind.Special, target, args); - } - - private void appendInvoke(InvokeKind invokeKind, RiResolvedMethod targetMethod, ValueNode[] args) { - CiKind resultType = targetMethod.signature().returnKind(false); - if (GraalOptions.DeoptALot) { - DeoptimizeNode deoptimize = currentGraph.add(new DeoptimizeNode(DeoptAction.None)); - 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())); - 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()))); - frameState.pushReturn(resultType, result); - } - } - } - - private RiResolvedType getExactType(RiResolvedType staticType, ValueNode receiver) { - RiResolvedType exact = staticType.exactType(); - if (exact == null) { - exact = receiver.exactType(); - if (exact == null) { - if (receiver.isConstant()) { - exact = runtime.getTypeOf(receiver.asConstant()); - } - if (exact == null) { - RiType declared = receiver.declaredType(); - if (declared instanceof RiResolvedType) { - exact = ((RiResolvedType) declared).exactType(); - } - } - } - } - return exact; - } - - private void callRegisterFinalizer() { - // append a call to the finalizer registration - append(currentGraph.add(new RegisterFinalizerNode(frameState.loadLocal(0)))); - } - - private void genReturn(ValueNode x) { - frameState.clearStack(); - if (x != null) { - frameState.push(x.kind(), x); - } - appendGoto(createTarget(returnBlock(bci()), frameState)); - } - - private MonitorEnterNode genMonitorEnter(ValueNode x) { - MonitorObject monitorObject = currentGraph.add(new MonitorObject(x)); - MonitorEnterNode monitorEnter = currentGraph.add(new MonitorEnterNode(monitorObject)); - frameState.lock(monitorObject); - appendWithBCI(monitorEnter); - return monitorEnter; - } - - private MonitorExitNode genMonitorExit(ValueNode x) { - if (frameState.locksSize() <= 0) { - throw new CiBailout("monitor stack underflow"); - } - MonitorObject monitorObject = frameState.lockAt(frameState.locksSize() - 1); - - // We only compile methods with balanced monitors. However, x might be a phi function - // that can be optimized away later on, so we have to disable the check for phi functions. - assert x == monitorObject.owner() || x instanceof PhiNode; - - MonitorExitNode monitorExit = currentGraph.add(new MonitorExitNode(monitorObject)); - appendWithBCI(monitorExit); - frameState.unlock(monitorObject); - return monitorExit; - } - - private void genJsr(int dest) { - Block successor = currentBlock.jsrSuccessor; - assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci(); - JsrScope scope = currentBlock.jsrScope; - if (!successor.jsrScope.pop().equals(scope)) { - throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); - } - if (successor.jsrScope.nextReturnAddress() != stream().nextBCI()) { - throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); - } - frameState.push(CiKind.Jsr, ConstantNode.forJsr(stream().nextBCI(), currentGraph)); - appendGoto(createTarget(successor, frameState)); - } - - private void genRet(int localIndex) { - Block successor = currentBlock.retSuccessor; - ValueNode local = frameState.loadLocal(localIndex); - JsrScope scope = currentBlock.jsrScope; - int retAddress = scope.nextReturnAddress(); - append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new CompareNode(local, Condition.EQ, ConstantNode.forJsr(retAddress, currentGraph)))))); - if (!successor.jsrScope.equals(scope.pop())) { - throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)"); - } - appendGoto(createTarget(successor, frameState)); - } - - private void genTableswitch() { - int bci = bci(); - ValueNode value = frameState.ipop(); - BytecodeTableSwitch ts = new BytecodeTableSwitch(stream(), bci); - - int nofCases = ts.numberOfCases() + 1; // including default case - assert currentBlock.normalSuccessors == nofCases; - - TableSwitchNode tableSwitch = currentGraph.add(new TableSwitchNode(value, ts.lowKey(), switchProbability(nofCases, bci))); - for (int i = 0; i < nofCases; ++i) { - tableSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); - } - append(tableSwitch); - } - - private double[] switchProbability(int numberOfCases, int bci) { - double[] prob = method.switchProbability(bci); - if (prob != null) { - assert prob.length == numberOfCases; - } else { - if (GraalOptions.TraceProbability) { - TTY.println("Missing probability (switch) in " + method + " at bci " + bci); - } - prob = new double[numberOfCases]; - for (int i = 0; i < numberOfCases; i++) { - prob[i] = 1.0d / numberOfCases; - } - } - return prob; - } - - private void genLookupswitch() { - int bci = bci(); - ValueNode value = frameState.ipop(); - BytecodeLookupSwitch ls = new BytecodeLookupSwitch(stream(), bci); - - int nofCases = ls.numberOfCases() + 1; // including default case - assert currentBlock.normalSuccessors == nofCases; - - int[] keys = new int[nofCases - 1]; - for (int i = 0; i < nofCases - 1; ++i) { - keys[i] = ls.keyAt(i); - } - LookupSwitchNode lookupSwitch = currentGraph.add(new LookupSwitchNode(value, keys, switchProbability(nofCases, bci))); - for (int i = 0; i < nofCases; ++i) { - lookupSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); - } - append(lookupSwitch); - } - - private ConstantNode appendConstant(CiConstant constant) { - return ConstantNode.forCiConstant(constant, runtime, currentGraph); - } - - private ValueNode append(FixedNode fixed) { - lastInstr.setNext(fixed); - lastInstr = null; - return fixed; - } - - private ValueNode append(FixedWithNextNode x) { - return appendWithBCI(x); - } - - private static ValueNode append(ValueNode v) { - return v; - } - - private ValueNode appendWithBCI(FixedWithNextNode x) { - assert x.predecessor() == null : "instruction should not have been appended yet"; - assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")"; - lastInstr.setNext(x); - lastInstr = x; - return x; - } - - private FixedNode createTarget(Block block, FrameStateAccess stateAfter) { - assert block != null && stateAfter != null; - assert block.isLoopHeader || block.firstInstruction == null || block.firstInstruction.next() == null : - "non-loop block must be iterated after all its predecessors. startBci=" + block.startBci + ", " + block.getClass().getSimpleName() + ", " + block.firstInstruction.next(); - - if (block.isExceptionEntry) { - assert stateAfter.stackSize() == 1; - } - - if (block.firstInstruction == null) { - if (block.isLoopHeader) { - LoopBeginNode loopBegin = currentGraph.add(new LoopBeginNode()); - loopBegin.addEnd(currentGraph.add(new EndNode())); - LoopEndNode loopEnd = currentGraph.add(new LoopEndNode()); - loopEnd.setLoopBegin(loopBegin); - PlaceholderNode pBegin = currentGraph.add(new PlaceholderNode()); - pBegin.setNext(loopBegin.forwardEdge()); - PlaceholderNode pEnd = currentGraph.add(new PlaceholderNode()); - pEnd.setNext(loopEnd); - loopBegin.setStateAfter(stateAfter.duplicate(block.startBci)); - block.firstInstruction = pBegin; - } else { - block.firstInstruction = currentGraph.add(new PlaceholderNode()); - } - } - mergeOrClone(block, stateAfter); - addToWorkList(block); - - FixedNode result = null; - if (block.isLoopHeader && isVisited(block)) { - result = (FixedNode) loopBegin(block).loopEnd().predecessor(); - } else { - result = block.firstInstruction; - } - - assert result instanceof MergeNode || result instanceof PlaceholderNode : result; - if (result instanceof MergeNode) { - if (result instanceof LoopBeginNode) { - result = ((LoopBeginNode) result).forwardEdge(); - } else { - EndNode end = currentGraph.add(new EndNode()); - ((MergeNode) result).addEnd(end); - PlaceholderNode p = currentGraph.add(new PlaceholderNode()); - int bci = block.startBci; - if (block instanceof ExceptionBlock) { - bci = ((ExceptionBlock) block).deoptBci; - } - p.setStateAfter(stateAfter.duplicate(bci)); - p.setNext(end); - result = p; - } - } - assert !(result instanceof LoopBeginNode || result instanceof MergeNode); - return result; - } - - private ValueNode synchronizedObject(FrameStateAccess state, RiResolvedMethod target) { - if (isStatic(target.accessFlags())) { - return append(ConstantNode.forCiConstant(target.holder().getEncoding(Representation.JavaClass), runtime, currentGraph)); - } else { - return state.localAt(0); - } - } - - private void iterateAllBlocks() { - Block block; - while ((block = removeFromWorkList()) != null) { - // remove blocks that have no predecessors by the time it their bytecodes are parsed - if (block.firstInstruction == null) { - markVisited(block); - continue; - } - - if (!isVisited(block)) { - markVisited(block); - // now parse the block - if (block.isLoopHeader) { - LoopBeginNode begin = loopBegin(block); - FrameState preLoopState = ((StateSplit) block.firstInstruction).stateAfter(); - assert preLoopState != null; - FrameState duplicate = preLoopState.duplicate(preLoopState.bci); - begin.setStateAfter(duplicate); - duplicate.insertLoopPhis(begin); - lastInstr = begin; - } else { - lastInstr = block.firstInstruction; - } - frameState.initializeFrom(((StateSplit) lastInstr).stateAfter()); - assert lastInstr.next() == null : "instructions already appended at block " + block.blockID; - - if (block == returnBlock) { - createReturn(); - } else if (block == unwindBlock) { - createUnwind(); - } else if (block instanceof ExceptionBlock) { - createExceptionDispatch((ExceptionBlock) block); - } else if (block instanceof DeoptBlock) { - createDeopt(); - } else { - frameState.setRethrowException(false); - iterateBytecodesForBlock(block); - } - } - } - } - - private void connectLoopEndToBegin() { - for (LoopBeginNode begin : currentGraph.getNodes(LoopBeginNode.class)) { - LoopEndNode loopEnd = begin.loopEnd(); - AbstractStateSplit loopEndStateSplit = (AbstractStateSplit) loopEnd.predecessor(); - if (loopEndStateSplit.stateAfter() != null) { - begin.stateAfter().mergeLoop(begin, loopEndStateSplit.stateAfter()); - } else { -// This can happen with degenerated loops like this one: -// for (;;) { -// try { -// break; -// } catch (UnresolvedException iioe) { -// } -// } - // Delete the phis (all of them must have exactly one input). - for (PhiNode phi : begin.phis().snapshot()) { - assert phi.valueCount() == 1; - begin.stateAfter().deleteRedundantPhi(phi, phi.firstValue()); - } - - // Delete the loop end. - loopEndStateSplit.safeDelete(); - loopEnd.safeDelete(); - - // Remove the loop begin. - EndNode loopEntryEnd = begin.forwardEdge(); - FixedNode beginSucc = begin.next(); - FrameState stateAfter = begin.stateAfter(); - stateAfter.delete(); - begin.safeDelete(); - loopEntryEnd.replaceAndDelete(beginSucc); - } - } - } - - private static LoopBeginNode loopBegin(Block block) { - EndNode endNode = (EndNode) block.firstInstruction.next(); - LoopBeginNode loopBegin = (LoopBeginNode) endNode.merge(); - return loopBegin; - } - - private void createDeopt() { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile))); - } - - private void createUnwind() { - synchronizedEpilogue(FrameState.AFTER_EXCEPTION_BCI); - UnwindNode unwindNode = currentGraph.add(new UnwindNode(frameState.apop())); - append(unwindNode); - } - - private void createReturn() { - if (method.isConstructor() && method.holder().superType() == null) { - callRegisterFinalizer(); - } - CiKind returnKind = method.signature().returnKind(false).stackKind(); - ValueNode x = returnKind == CiKind.Void ? null : frameState.pop(returnKind); - assert frameState.stackSize() == 0; - - // TODO (gd) remove this when FloatingRead is fixed - if (Modifier.isSynchronized(method.accessFlags())) { - append(currentGraph.add(new ValueAnchorNode(x))); - } - - synchronizedEpilogue(FrameState.AFTER_BCI); - ReturnNode returnNode = currentGraph.add(new ReturnNode(x)); - append(returnNode); - } - - private void synchronizedEpilogue(int bci) { - if (Modifier.isSynchronized(method.accessFlags())) { - MonitorExitNode monitorExit = genMonitorExit(methodSynchronizedObject); - monitorExit.setStateAfter(frameState.create(bci)); - } - } - - 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; - - RiType catchType = block.handler.catchType(); - ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, catchType, (catchType instanceof RiResolvedType) && ((RiResolvedType) catchType).isInitialized()); - 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); - } - } - } - - private void appendGoto(FixedNode target) { - if (lastInstr != null) { - lastInstr.setNext(target); - } - } - - private void iterateBytecodesForBlock(Block block) { - assert frameState != null; - - currentBlock = block; - - int endBCI = stream.endBCI(); - - stream.setBCI(block.startBci); - int bci = block.startBci; - while (bci < endBCI) { - // read the opcode - int opcode = stream.currentBC(); - traceState(); - traceInstruction(bci, opcode, bci == block.startBci); - processBytecode(bci, opcode); - - if (lastInstr == null || IdentifyBlocksPhase.isBlockEnd(lastInstr) || lastInstr.next() != null) { - break; - } - - stream.next(); - bci = stream.currentBCI(); - if (lastInstr instanceof StateSplit) { - StateSplit stateSplit = (StateSplit) lastInstr; - if (stateSplit.stateAfter() == null && stateSplit.needsStateAfter()) { - stateSplit.setStateAfter(frameState.create(bci)); - } - } - if (bci < endBCI) { - if (bci > block.endBci) { - assert !block.successors.get(0).isExceptionEntry; - assert block.normalSuccessors == 1; - // we fell through to the next block, add a goto and break - appendGoto(createTarget(block.successors.get(0), frameState)); - break; - } - } - } - } - - private void traceState() { - if (GraalOptions.TraceBytecodeParserLevel >= TRACELEVEL_STATE && !TTY.isSuppressed()) { - log.println(String.format("| state [nr locals = %d, stack depth = %d, method = %s]", frameState.localsSize(), frameState.stackSize(), method)); - for (int i = 0; i < frameState.localsSize(); ++i) { - ValueNode value = frameState.localAt(i); - log.println(String.format("| local[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); - } - for (int i = 0; i < frameState.stackSize(); ++i) { - ValueNode value = frameState.stackAt(i); - log.println(String.format("| stack[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); - } - for (int i = 0; i < frameState.locksSize(); ++i) { - ValueNode value = frameState.lockAt(i); - log.println(String.format("| lock[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); - } - } - } - - private void processBytecode(int bci, int opcode) { - int cpi; - - // Checkstyle: stop - switch (opcode) { - case NOP : /* nothing to do */ break; - case ACONST_NULL : frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); break; - case ICONST_M1 : frameState.ipush(appendConstant(CiConstant.INT_MINUS_1)); break; - case ICONST_0 : frameState.ipush(appendConstant(CiConstant.INT_0)); break; - case ICONST_1 : frameState.ipush(appendConstant(CiConstant.INT_1)); break; - case ICONST_2 : frameState.ipush(appendConstant(CiConstant.INT_2)); break; - case ICONST_3 : frameState.ipush(appendConstant(CiConstant.INT_3)); break; - case ICONST_4 : frameState.ipush(appendConstant(CiConstant.INT_4)); break; - case ICONST_5 : frameState.ipush(appendConstant(CiConstant.INT_5)); break; - case LCONST_0 : frameState.lpush(appendConstant(CiConstant.LONG_0)); break; - case LCONST_1 : frameState.lpush(appendConstant(CiConstant.LONG_1)); break; - case FCONST_0 : frameState.fpush(appendConstant(CiConstant.FLOAT_0)); break; - case FCONST_1 : frameState.fpush(appendConstant(CiConstant.FLOAT_1)); break; - case FCONST_2 : frameState.fpush(appendConstant(CiConstant.FLOAT_2)); break; - case DCONST_0 : frameState.dpush(appendConstant(CiConstant.DOUBLE_0)); break; - case DCONST_1 : frameState.dpush(appendConstant(CiConstant.DOUBLE_1)); break; - case BIPUSH : frameState.ipush(appendConstant(CiConstant.forInt(stream.readByte()))); break; - case SIPUSH : frameState.ipush(appendConstant(CiConstant.forInt(stream.readShort()))); break; - case LDC : // fall through - case LDC_W : // fall through - case LDC2_W : genLoadConstant(stream.readCPI()); break; - case ILOAD : loadLocal(stream.readLocalIndex(), CiKind.Int); break; - case LLOAD : loadLocal(stream.readLocalIndex(), CiKind.Long); break; - case FLOAD : loadLocal(stream.readLocalIndex(), CiKind.Float); break; - case DLOAD : loadLocal(stream.readLocalIndex(), CiKind.Double); break; - case ALOAD : loadLocal(stream.readLocalIndex(), CiKind.Object); break; - case ILOAD_0 : // fall through - case ILOAD_1 : // fall through - case ILOAD_2 : // fall through - case ILOAD_3 : loadLocal(opcode - ILOAD_0, CiKind.Int); break; - case LLOAD_0 : // fall through - case LLOAD_1 : // fall through - case LLOAD_2 : // fall through - case LLOAD_3 : loadLocal(opcode - LLOAD_0, CiKind.Long); break; - case FLOAD_0 : // fall through - case FLOAD_1 : // fall through - case FLOAD_2 : // fall through - case FLOAD_3 : loadLocal(opcode - FLOAD_0, CiKind.Float); break; - case DLOAD_0 : // fall through - case DLOAD_1 : // fall through - case DLOAD_2 : // fall through - case DLOAD_3 : loadLocal(opcode - DLOAD_0, CiKind.Double); break; - case ALOAD_0 : // fall through - case ALOAD_1 : // fall through - case ALOAD_2 : // fall through - case ALOAD_3 : loadLocal(opcode - ALOAD_0, CiKind.Object); break; - case IALOAD : genLoadIndexed(CiKind.Int ); break; - case LALOAD : genLoadIndexed(CiKind.Long ); break; - case FALOAD : genLoadIndexed(CiKind.Float ); break; - case DALOAD : genLoadIndexed(CiKind.Double); break; - case AALOAD : genLoadIndexed(CiKind.Object); break; - case BALOAD : genLoadIndexed(CiKind.Byte ); break; - case CALOAD : genLoadIndexed(CiKind.Char ); break; - case SALOAD : genLoadIndexed(CiKind.Short ); break; - case ISTORE : storeLocal(CiKind.Int, stream.readLocalIndex()); break; - case LSTORE : storeLocal(CiKind.Long, stream.readLocalIndex()); break; - case FSTORE : storeLocal(CiKind.Float, stream.readLocalIndex()); break; - case DSTORE : storeLocal(CiKind.Double, stream.readLocalIndex()); break; - case ASTORE : storeLocal(CiKind.Object, stream.readLocalIndex()); break; - case ISTORE_0 : // fall through - case ISTORE_1 : // fall through - case ISTORE_2 : // fall through - case ISTORE_3 : storeLocal(CiKind.Int, opcode - ISTORE_0); break; - case LSTORE_0 : // fall through - case LSTORE_1 : // fall through - case LSTORE_2 : // fall through - case LSTORE_3 : storeLocal(CiKind.Long, opcode - LSTORE_0); break; - case FSTORE_0 : // fall through - case FSTORE_1 : // fall through - case FSTORE_2 : // fall through - case FSTORE_3 : storeLocal(CiKind.Float, opcode - FSTORE_0); break; - case DSTORE_0 : // fall through - case DSTORE_1 : // fall through - case DSTORE_2 : // fall through - case DSTORE_3 : storeLocal(CiKind.Double, opcode - DSTORE_0); break; - case ASTORE_0 : // fall through - case ASTORE_1 : // fall through - case ASTORE_2 : // fall through - case ASTORE_3 : storeLocal(CiKind.Object, opcode - ASTORE_0); break; - case IASTORE : genStoreIndexed(CiKind.Int ); break; - case LASTORE : genStoreIndexed(CiKind.Long ); break; - case FASTORE : genStoreIndexed(CiKind.Float ); break; - case DASTORE : genStoreIndexed(CiKind.Double); break; - case AASTORE : genStoreIndexed(CiKind.Object); break; - case BASTORE : genStoreIndexed(CiKind.Byte ); break; - case CASTORE : genStoreIndexed(CiKind.Char ); break; - case SASTORE : genStoreIndexed(CiKind.Short ); break; - case POP : // fall through - case POP2 : // fall through - case DUP : // fall through - case DUP_X1 : // fall through - case DUP_X2 : // fall through - case DUP2 : // fall through - case DUP2_X1 : // fall through - case DUP2_X2 : // fall through - case SWAP : stackOp(opcode); break; - case IADD : // fall through - case ISUB : // fall through - case IMUL : genArithmeticOp(CiKind.Int, opcode, false); break; - case IDIV : // fall through - case IREM : genArithmeticOp(CiKind.Int, opcode, true); break; - case LADD : // fall through - case LSUB : // fall through - case LMUL : genArithmeticOp(CiKind.Long, opcode, false); break; - case LDIV : // fall through - case LREM : genArithmeticOp(CiKind.Long, opcode, true); break; - case FADD : // fall through - case FSUB : // fall through - case FMUL : // fall through - case FDIV : // fall through - case FREM : genArithmeticOp(CiKind.Float, opcode, false); break; - case DADD : // fall through - case DSUB : // fall through - case DMUL : // fall through - case DDIV : // fall through - case DREM : genArithmeticOp(CiKind.Double, opcode, false); break; - case INEG : genNegateOp(CiKind.Int); break; - case LNEG : genNegateOp(CiKind.Long); break; - case FNEG : genNegateOp(CiKind.Float); break; - case DNEG : genNegateOp(CiKind.Double); break; - case ISHL : // fall through - case ISHR : // fall through - case IUSHR : genShiftOp(CiKind.Int, opcode); break; - case IAND : // fall through - case IOR : // fall through - case IXOR : genLogicOp(CiKind.Int, opcode); break; - case LSHL : // fall through - case LSHR : // fall through - case LUSHR : genShiftOp(CiKind.Long, opcode); break; - case LAND : // fall through - case LOR : // fall through - case LXOR : genLogicOp(CiKind.Long, opcode); break; - case IINC : genIncrement(); break; - case I2L : genConvert(ConvertNode.Op.I2L); break; - case I2F : genConvert(ConvertNode.Op.I2F); break; - case I2D : genConvert(ConvertNode.Op.I2D); break; - case L2I : genConvert(ConvertNode.Op.L2I); break; - case L2F : genConvert(ConvertNode.Op.L2F); break; - case L2D : genConvert(ConvertNode.Op.L2D); break; - case F2I : genConvert(ConvertNode.Op.F2I); break; - case F2L : genConvert(ConvertNode.Op.F2L); break; - case F2D : genConvert(ConvertNode.Op.F2D); break; - case D2I : genConvert(ConvertNode.Op.D2I); break; - case D2L : genConvert(ConvertNode.Op.D2L); break; - case D2F : genConvert(ConvertNode.Op.D2F); break; - case I2B : genConvert(ConvertNode.Op.I2B); break; - case I2C : genConvert(ConvertNode.Op.I2C); break; - case I2S : genConvert(ConvertNode.Op.I2S); break; - case LCMP : genCompareOp(CiKind.Long, false); break; - case FCMPL : genCompareOp(CiKind.Float, true); break; - case FCMPG : genCompareOp(CiKind.Float, false); break; - case DCMPL : genCompareOp(CiKind.Double, true); break; - case DCMPG : genCompareOp(CiKind.Double, false); break; - case IFEQ : genIfZero(Condition.EQ); break; - case IFNE : genIfZero(Condition.NE); break; - case IFLT : genIfZero(Condition.LT); break; - case IFGE : genIfZero(Condition.GE); break; - case IFGT : genIfZero(Condition.GT); break; - case IFLE : genIfZero(Condition.LE); break; - case IF_ICMPEQ : genIfSame(CiKind.Int, Condition.EQ); break; - case IF_ICMPNE : genIfSame(CiKind.Int, Condition.NE); break; - case IF_ICMPLT : genIfSame(CiKind.Int, Condition.LT); break; - case IF_ICMPGE : genIfSame(CiKind.Int, Condition.GE); break; - case IF_ICMPGT : genIfSame(CiKind.Int, Condition.GT); break; - case IF_ICMPLE : genIfSame(CiKind.Int, Condition.LE); break; - case IF_ACMPEQ : genIfSame(CiKind.Object, Condition.EQ); break; - case IF_ACMPNE : genIfSame(CiKind.Object, Condition.NE); break; - case GOTO : genGoto(); break; - case JSR : genJsr(stream.readBranchDest()); break; - case RET : genRet(stream.readLocalIndex()); break; - case TABLESWITCH : genTableswitch(); break; - case LOOKUPSWITCH : genLookupswitch(); break; - case IRETURN : genReturn(frameState.ipop()); break; - case LRETURN : genReturn(frameState.lpop()); break; - case FRETURN : genReturn(frameState.fpop()); break; - case DRETURN : genReturn(frameState.dpop()); break; - case ARETURN : genReturn(frameState.apop()); break; - case RETURN : genReturn(null); break; - case GETSTATIC : cpi = stream.readCPI(); genGetStatic(lookupField(cpi, opcode)); break; - case PUTSTATIC : cpi = stream.readCPI(); genPutStatic(lookupField(cpi, opcode)); break; - case GETFIELD : cpi = stream.readCPI(); genGetField(lookupField(cpi, opcode)); break; - case PUTFIELD : cpi = stream.readCPI(); genPutField(lookupField(cpi, opcode)); break; - case INVOKEVIRTUAL : cpi = stream.readCPI(); genInvokeVirtual(lookupMethod(cpi, opcode)); break; - case INVOKESPECIAL : cpi = stream.readCPI(); genInvokeSpecial(lookupMethod(cpi, opcode)); break; - case INVOKESTATIC : cpi = stream.readCPI(); genInvokeStatic(lookupMethod(cpi, opcode)); break; - case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(lookupMethod(cpi, opcode)); break; - case NEW : genNewInstance(stream.readCPI()); break; - case NEWARRAY : genNewTypeArray(stream.readLocalIndex()); break; - case ANEWARRAY : genNewObjectArray(stream.readCPI()); break; - case ARRAYLENGTH : genArrayLength(); break; - case ATHROW : genThrow(stream.currentBCI()); break; - case CHECKCAST : genCheckCast(); break; - case INSTANCEOF : genInstanceOf(); break; - case MONITORENTER : genMonitorEnter(frameState.apop()); break; - case MONITOREXIT : genMonitorExit(frameState.apop()); break; - case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break; - case IFNULL : genIfNull(Condition.EQ); break; - case IFNONNULL : genIfNull(Condition.NE); break; - case GOTO_W : genGoto(); break; - case JSR_W : genJsr(stream.readFarBranchDest()); break; - case BREAKPOINT: - throw new CiBailout("concurrent setting of breakpoint"); - default: - throw new CiBailout("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]"); - } - // Checkstyle: resume - } - - private void traceInstruction(int bci, int opcode, boolean blockStart) { - if (GraalOptions.TraceBytecodeParserLevel >= TRACELEVEL_INSTRUCTIONS && !TTY.isSuppressed()) { - StringBuilder sb = new StringBuilder(40); - sb.append(blockStart ? '+' : '|'); - if (bci < 10) { - sb.append(" "); - } else if (bci < 100) { - sb.append(' '); - } - sb.append(bci).append(": ").append(Bytecodes.nameOf(opcode)); - for (int i = bci + 1; i < stream.nextBCI(); ++i) { - sb.append(' ').append(stream.readUByte(i)); - } - if (!currentBlock.jsrScope.isEmpty()) { - sb.append(' ').append(currentBlock.jsrScope); - } - log.println(sb.toString()); - } - } - - private void genArrayLength() { - frameState.ipush(append(currentGraph.add(new ArrayLengthNode(frameState.apop())))); - } - - /** - * Adds a block to the worklist, if it is not already in the worklist. - * This method will keep the worklist topologically stored (i.e. the lower - * DFNs are earlier in the list). - * @param block the block to add to the work list - */ - private void addToWorkList(Block block) { - if (!isOnWorkList(block)) { - markOnWorkList(block); - sortIntoWorkList(block); - } - } - - private void sortIntoWorkList(Block top) { - workList.offer(top); - } - - /** - * Removes the next block from the worklist. The list is sorted topologically, so the - * block with the lowest depth first number in the list will be removed and returned. - * @return the next block from the worklist; {@code null} if there are no blocks - * in the worklist - */ - private Block removeFromWorkList() { - return workList.poll(); - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/JsrNotSupportedBailout.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/JsrNotSupportedBailout.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -import com.oracle.max.cri.ci.*; - - -public class JsrNotSupportedBailout extends CiBailout{ - private static final long serialVersionUID = -7476925652727154272L; - - public JsrNotSupportedBailout(String reason) { - super(reason); - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/JsrScope.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/JsrScope.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.max.graal.compiler.graphbuilder; - -public class JsrScope { - - public static final JsrScope EMPTY_SCOPE = new JsrScope(); - - private final long scope; - - private JsrScope(long scope) { - this.scope = scope; - } - - public JsrScope() { - this.scope = 0; - } - - public int nextReturnAddress() { - return (int) (scope & 0xffff); - } - - public JsrScope push(int jsrReturnBci) { - if ((scope & 0xffff000000000000L) != 0) { - throw new JsrNotSupportedBailout("only four jsr nesting levels are supported"); - } - return new JsrScope((scope << 16) | jsrReturnBci); - } - - public boolean isEmpty() { - return scope == 0; - } - - public JsrScope pop() { - return new JsrScope(scope >>> 16); - } - - @Override - public int hashCode() { - return (int) (scope ^ (scope >>> 32)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - return obj != null && getClass() == obj.getClass() && scope == ((JsrScope) obj).scope; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - long tmp = scope; - sb.append(" ["); - while (tmp != 0) { - sb.append(", ").append(tmp & 0xffff); - tmp = tmp >>> 16; - } - sb.append(']'); - return sb.toString(); - } -} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/package-info.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graphbuilder/package-info.java Tue Jan 03 17:53:26 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - */ -package com.oracle.max.graal.compiler.graphbuilder; diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Tue Jan 03 18:22:10 2012 +0100 @@ -28,7 +28,6 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.criutils.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.phases.PhasePlan.PhasePosition; import com.oracle.max.graal.compiler.util.*; import com.oracle.max.graal.compiler.util.InliningUtil.InlineInfo; @@ -213,7 +212,9 @@ if (GraalOptions.ParseBeforeInlining) { if (!parsedMethods.containsKey(method)) { StructuredGraph newGraph = new StructuredGraph(method); - new GraphBuilderPhase(runtime, null).apply(newGraph, currentContext, false); + if (plan != null) { + plan.runPhases(PhasePosition.AFTER_PARSING, newGraph, currentContext); + } new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph, currentContext, false); count = graphComplexity(newGraph); parsedMethods.put(method, count); diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java Tue Jan 03 18:22:10 2012 +0100 @@ -29,7 +29,6 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.criutils.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.cri.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.nodes.*; @@ -110,17 +109,17 @@ @Override public Node inline(StructuredGraph compilerGraph, GraalRuntime runtime, InliningCallback callback) { - StructuredGraph graph = GraphBuilderPhase.cachedGraphs.get(concrete); - if (graph != null) { - if (GraalOptions.TraceInlining) { - TTY.println("Reusing graph for %s", methodName(concrete, invoke)); - } - } else { + StructuredGraph graph = null; // TODO: Solve graph caching differently! GraphBuilderPhase.cachedGraphs.get(concrete); +// if (graph != null) { +// if (GraalOptions.TraceInlining) { +// TTY.println("Reusing graph for %s", methodName(concrete, invoke)); +// } +// } else { if (GraalOptions.TraceInlining) { TTY.println("Building graph for %s, locals: %d, stack: %d", methodName(concrete, invoke), concrete.maxLocals(), concrete.maxStackSize()); } graph = callback.buildGraph(concrete); - } +// } return InliningUtil.inline(invoke, graph, true); } diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotSignature.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotSignature.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotSignature.java Tue Jan 03 18:22:10 2012 +0100 @@ -26,8 +26,8 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.hotspot.ri.*; +import com.oracle.max.graal.java.*; /** * Represents a method signature. @@ -35,7 +35,7 @@ public class HotSpotSignature extends CompilerObject implements RiSignature { /** - * + * */ private static final long serialVersionUID = -2890917956072366116L; private final List arguments = new ArrayList<>(); diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/VMExitsNative.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/VMExitsNative.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/VMExitsNative.java Tue Jan 03 18:22:10 2012 +0100 @@ -31,11 +31,11 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.criutils.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.phases.*; import com.oracle.max.graal.compiler.phases.PhasePlan.*; import com.oracle.max.graal.hotspot.ri.*; import com.oracle.max.graal.hotspot.server.*; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.snippets.*; /** diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BlockMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BlockMap.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import static com.oracle.max.graal.java.Bytecodes.*; + +import java.util.*; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.compiler.*; +import com.oracle.max.graal.nodes.*; + +/** + * 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 BlockMap#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. + */ +public final class BlockMap { + + public static class Block implements Cloneable { + public int startBci; + public int endBci; + public boolean isExceptionEntry; + public boolean isLoopHeader; + public int blockID; + + public FixedWithNextNode firstInstruction; + + public ArrayList successors = new ArrayList<>(2); + public int normalSuccessors; + + private boolean visited; + private boolean active; + public long loops; + + public HashMap jsrAlternatives; + public JsrScope jsrScope = JsrScope.EMPTY_SCOPE; + public Block jsrSuccessor; + public int jsrReturnBci; + public Block retSuccessor; + public boolean endsWithRet = false; + + public Block copy() { + try { + Block block = (Block) super.clone(); + block.successors = new ArrayList<>(successors); + return block; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + } + + public static class ExceptionBlock extends Block { + public RiExceptionHandler handler; + public int deoptBci; + } + + public static class DeoptBlock extends Block { + public DeoptBlock(int startBci) { + this.startBci = startBci; + } + } + + /** + * The blocks found in this method, in reverse postorder. + */ + public final List blocks; + + public final RiResolvedMethod method; + + private final RiExceptionHandler[] exceptionHandlers; + + private Block[] blockMap; + + public final BitSet canTrap; + + public boolean hasJsrBytecodes; + + public Block startBlock; + + public final boolean useBranchPrediction; + + /** + * Creates a new BlockMap instance from bytecode of the given method . + * @param method the compiler interface method containing the code + */ + public BlockMap(RiResolvedMethod method, boolean useBranchPrediction) { + this.method = method; + exceptionHandlers = method.exceptionHandlers(); + this.blockMap = new Block[method.codeSize()]; + this.canTrap = new BitSet(blockMap.length); + this.blocks = new ArrayList<>(); + this.useBranchPrediction = useBranchPrediction; + } + + 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"); + } + createJsrAlternatives(blockMap[0]); + } + computeBlockOrder(); + + initializeBlockIds(); + + startBlock = blockMap[0]; + + // Discard big arrays so that they can be GCed + blockMap = null; + } + + private void initializeBlockIds() { + for (int i = 0; i < blocks.size(); i++) { + blocks.get(i).blockID = i; + } + } + + private void makeExceptionEntries() { + // start basic blocks at all exception handler blocks and mark them as exception entries + for (RiExceptionHandler h : this.exceptionHandlers) { + Block xhandler = makeBlock(h.handlerBCI()); + xhandler.isExceptionEntry = true; + } + } + + private void iterateOverBytecodes() { + // 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) + byte[] code = method.code(); + Block current = null; + int bci = 0; + while (bci < code.length) { + if (current == null || blockMap[bci] != null) { + Block b = makeBlock(bci); + if (current != null) { + setSuccessors(current.endBci, b); + } + current = b; + } + blockMap[bci] = current; + current.endBci = bci; + + int opcode = Bytes.beU1(code, bci); + switch (opcode) { + case IRETURN: // fall through + case LRETURN: // fall through + case FRETURN: // fall through + case DRETURN: // fall through + case ARETURN: // fall through + case RETURN: { + current = null; + break; + } + case ATHROW: { + current = null; + canTrap.set(bci); + 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; + double probability = useBranchPrediction ? method.branchProbability(bci) : -1; + + Block b1 = probability == 0.0 ? new DeoptBlock(bci + Bytes.beS2(code, bci + 1)) : makeBlock(bci + Bytes.beS2(code, bci + 1)); + Block b2 = probability == 1.0 ? new DeoptBlock(bci + 3) : makeBlock(bci + 3); + setSuccessors(bci, b1, b2); + break; + } + case GOTO: + case GOTO_W: { + current = null; + int target = bci + Bytes.beSVar(code, bci + 1, opcode == GOTO_W); + Block b1 = makeBlock(target); + setSuccessors(bci, b1); + break; + } + case TABLESWITCH: { + current = null; + BytecodeTableSwitch sw = new BytecodeTableSwitch(code, bci); + setSuccessors(bci, makeSwitchSuccessors(sw)); + break; + } + case LOOKUPSWITCH: { + current = null; + BytecodeLookupSwitch sw = new BytecodeLookupSwitch(code, bci); + setSuccessors(bci, makeSwitchSuccessors(sw)); + break; + } + case JSR: + case JSR_W: { + hasJsrBytecodes = true; + int target = bci + Bytes.beSVar(code, bci + 1, opcode == JSR_W); + if (target == 0) { + throw new JsrNotSupportedBailout("jsr target bci 0 not allowed"); + } + Block b1 = makeBlock(target); + current.jsrSuccessor = b1; + current.jsrReturnBci = bci + lengthOf(opcode); + current = null; + setSuccessors(bci, b1); + break; + } + case RET: { + current.endsWithRet = true; + current = null; + break; + } + case WIDE: { + int opcode2 = Bytes.beU1(code, bci); + switch (opcode2) { + case RET: { + current.endsWithRet = true; + current = null; + break; + } + } + break; + } + case INVOKEINTERFACE: + case INVOKESPECIAL: + case INVOKESTATIC: + case INVOKEVIRTUAL: { + current = null; + int target = bci + lengthOf(code, bci); + Block b1 = makeBlock(target); + setSuccessors(bci, b1); + canTrap.set(bci); + break; + } + default: { + if (canTrap(opcode, bci)) { + canTrap.set(bci); + } + } + } + bci += lengthOf(code, bci); + } + } + + public boolean canTrap(int opcode, int bci) { + switch (opcode) { + case INVOKESTATIC: + case INVOKESPECIAL: + case INVOKEVIRTUAL: + case INVOKEINTERFACE: { + return true; + } + 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 PUTFIELD: + case GETFIELD: { + if (GraalOptions.AllowExplicitExceptionChecks) { + return method.exceptionProbability(bci) > 0; + } + } + } + return false; + } + + private Block makeBlock(int startBci) { + Block oldBlock = blockMap[startBci]; + if (oldBlock == null) { + Block newBlock = new Block(); + 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. + Block newBlock = new Block(); + 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; + } + return newBlock; + + } else { + return oldBlock; + } + } + + 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)); + } + successors[max] = makeBlock(tswitch.defaultTarget()); + return successors; + } + + private void setSuccessors(int predBci, Block... successors) { + 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); + } + predecessor.normalSuccessors = successors.length; + } + + private final HashSet jsrVisited = new HashSet<>(); + + private void createJsrAlternatives(Block block) { + jsrVisited.add(block); + JsrScope scope = block.jsrScope; + + if (block.endsWithRet) { + block.retSuccessor = blockMap[scope.nextReturnAddress()]; + block.successors.add(block.retSuccessor); + assert block.retSuccessor != block.jsrSuccessor; + } + + if (block.jsrSuccessor != null || !scope.isEmpty()) { + for (int i = 0; i < block.successors.size(); i++) { + Block successor = block.successors.get(i); + JsrScope nextScope = scope; + if (successor == block.jsrSuccessor) { + nextScope = scope.push(block.jsrReturnBci); + } + if (successor == block.retSuccessor) { + nextScope = scope.pop(); + } + if (!successor.jsrScope.isEmpty()) { + throw new JsrNotSupportedBailout("unstructured control flow (" + successor.jsrScope + " " + nextScope + ")"); + } + if (!nextScope.isEmpty()) { + Block clone; + if (successor.jsrAlternatives != null && successor.jsrAlternatives.containsKey(nextScope)) { + clone = successor.jsrAlternatives.get(nextScope); + } else { + if (successor.jsrAlternatives == null) { + successor.jsrAlternatives = new HashMap<>(); + } + clone = successor.copy(); + clone.jsrScope = nextScope; + successor.jsrAlternatives.put(nextScope, clone); + } + block.successors.set(i, clone); + if (successor == block.jsrSuccessor) { + block.jsrSuccessor = clone; + } + if (successor == block.retSuccessor) { + block.retSuccessor = clone; + } + } + } + } + for (Block successor : block.successors) { + if (!jsrVisited.contains(successor)) { + createJsrAlternatives(successor); + } + } + } + + private HashMap exceptionDispatch = new HashMap<>(); + + 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<>(); + } + handlers.add(h); + if (h.isCatchAll()) { + break; + } + } + } + if (handlers != null) { + Block dispatch = makeExceptionDispatch(handlers, 0, bci); + block.successors.add(dispatch); + } + } + } + + private void computeBlockOrder() { + 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 CiBailout("Non-reducible loop"); + } + + // Convert postorder to the desired reverse postorder. + Collections.reverse(blocks); + } + + /** + * 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(Block 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 CiBailout("Loop formed by an exception handler"); + } + if (nextLoop >= Long.SIZE) { + // This restriction can be removed by using a fall-back to a BitSet in case we have more than 32 loops + // Don't compile such methods for now, until we see a concrete case that allows checking for correctness. + throw new CiBailout("Too many loops in method"); + } + + assert block.loops == 0; + block.loops = (long) 1 << (long) nextLoop; + nextLoop++; + } + assert Long.bitCount(block.loops) == 1; + } + + /** + * Depth-first traversal of the control flow graph. The flag {@linkplain Block#visited} is used to + * visit every block only once. The flag {@linkplain Block#active} is used to detect cycles (backward + * edges). + */ + private long computeBlockOrder(Block block) { + if (block.visited) { + if (block.active) { + // Reached block via backward branch. + makeLoopHeader(block); + } + // Return cached loop information for this block. + return block.loops; + } + + block.visited = true; + block.active = true; + + int loops = 0; + for (Block successor : block.successors) { + // Recursively process successors. + loops |= computeBlockOrder(successor); + } + + if (block.isLoopHeader) { + assert Long.bitCount(block.loops) == 1; + loops &= ~block.loops; + } + + block.loops = loops; + block.active = false; + blocks.add(block); + + return loops; + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeLookupSwitch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeLookupSwitch.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +/** + * A utility for processing {@link Bytecodes#LOOKUPSWITCH} bytecodes. + */ +class BytecodeLookupSwitch extends BytecodeSwitch { + private static final int OFFSET_TO_NUMBER_PAIRS = 4; + private static final int OFFSET_TO_FIRST_PAIR_MATCH = 8; + private static final int OFFSET_TO_FIRST_PAIR_OFFSET = 12; + private static final int PAIR_SIZE = 8; + + /** + * Constructor for a {@link BytecodeStream}. + * @param stream the {@code BytecodeStream} containing the switch instruction + * @param bci the index in the stream of the switch instruction + */ + public BytecodeLookupSwitch(BytecodeStream stream, int bci) { + super(stream, bci); + } + + /** + * Constructor for a bytecode array. + * @param code the bytecode array containing the switch instruction. + * @param bci the index in the array of the switch instruction + */ + public BytecodeLookupSwitch(byte[] code, int bci) { + super(code, bci); + } + + @Override + public int defaultOffset() { + return readWord(alignedBci); + } + + @Override + public int offsetAt(int i) { + return readWord(alignedBci + OFFSET_TO_FIRST_PAIR_OFFSET + PAIR_SIZE * i); + } + + @Override + public int keyAt(int i) { + return readWord(alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * i); + } + + @Override + public int numberOfCases() { + return readWord(alignedBci + OFFSET_TO_NUMBER_PAIRS); + } + + @Override + public int size() { + return alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * numberOfCases() - bci; + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeStream.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +/** + * A utility class that makes iterating over bytecodes and reading operands + * simpler and less error prone. For example, it handles the {@link Bytecodes#WIDE} instruction + * and wide variants of instructions internally. + */ +public final class BytecodeStream { + + private final byte[] code; + private int opcode; + private int curBCI; + private int nextBCI; + + /** + * Creates a new {@code BytecodeStream} for the specified bytecode. + * @param code the array of bytes that contains the bytecode + */ + public BytecodeStream(byte[] code) { + assert code != null; + this.code = code; + setBCI(0); + } + + /** + * Advances to the next bytecode. + */ + public void next() { + setBCI(nextBCI); + } + + /** + * Gets the next bytecode index (no side-effects). + * @return the next bytecode index + */ + public int nextBCI() { + return nextBCI; + } + + /** + * Gets the current bytecode index. + * @return the current bytecode index + */ + public int currentBCI() { + return curBCI; + } + + /** + * Gets the bytecode index of the end of the code. + * @return the index of the end of the code + */ + public int endBCI() { + return code.length; + } + + /** + * Gets the current opcode. This method will never return the + * {@link Bytecodes#WIDE WIDE} opcode, but will instead + * return the opcode that is modified by the {@code WIDE} opcode. + * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code + */ + public int currentBC() { + if (opcode == Bytecodes.WIDE) { + return Bytes.beU1(code, curBCI + 1); + } else { + return opcode; + } + } + + /** + * Reads the index of a local variable for one of the load or store instructions. + * The WIDE modifier is handled internally. + * @return the index of the local variable + */ + public int readLocalIndex() { + // read local variable index for load/store + if (opcode == Bytecodes.WIDE) { + return Bytes.beU2(code, curBCI + 2); + } + return Bytes.beU1(code, curBCI + 1); + } + + /** + * Read the delta for an {@link Bytecodes#IINC} bytecode. + * @return the delta for the {@code IINC} + */ + public int readIncrement() { + // read the delta for the iinc bytecode + if (opcode == Bytecodes.WIDE) { + return Bytes.beS2(code, curBCI + 4); + } + return Bytes.beS1(code, curBCI + 2); + } + + /** + * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions. + * @return the destination bytecode index + */ + public int readBranchDest() { + // reads the destination for a branch bytecode + return curBCI + Bytes.beS2(code, curBCI + 1); + } + + /** + * Read the destination of a {@link Bytecodes#GOTO_W} or {@link Bytecodes#JSR_W} instructions. + * @return the destination bytecode index + */ + public int readFarBranchDest() { + // reads the destination for a wide branch bytecode + return curBCI + Bytes.beS4(code, curBCI + 1); + } + + /** + * Read a signed 4-byte integer from the bytecode stream at the specified bytecode index. + * @param bci the bytecode index + * @return the integer value + */ + public int readInt(int bci) { + // reads a 4-byte signed value + return Bytes.beS4(code, bci); + } + + /** + * Reads an unsigned, 1-byte value from the bytecode stream at the specified bytecode index. + * @param bci the bytecode index + * @return the byte + */ + public int readUByte(int bci) { + return Bytes.beU1(code, bci); + } + + /** + * Reads a constant pool index for the current instruction. + * @return the constant pool index + */ + public char readCPI() { + if (opcode == Bytecodes.LDC) { + return (char) Bytes.beU1(code, curBCI + 1); + } + return (char) Bytes.beU2(code, curBCI + 1); + } + + /** + * Reads a signed, 1-byte value for the current instruction (e.g. BIPUSH). + * @return the byte + */ + public byte readByte() { + return code[curBCI + 1]; + } + + /** + * Reads a signed, 2-byte short for the current instruction (e.g. SIPUSH). + * @return the short value + */ + public short readShort() { + return (short) Bytes.beS2(code, curBCI + 1); + } + + /** + * Sets the bytecode index to the specified value. + * If {@code bci} is beyond the end of the array, {@link #currentBC} will return + * {@link Bytecodes#END} and other methods may throw {@link ArrayIndexOutOfBoundsException}. + * @param bci the new bytecode index + */ + public void setBCI(int bci) { + curBCI = bci; + if (curBCI < code.length) { + opcode = Bytes.beU1(code, bci); + nextBCI = bci + Bytecodes.lengthOf(code, bci); + } else { + opcode = Bytecodes.END; + nextBCI = curBCI; + } + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeSwitch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeSwitch.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +/** + * An abstract class that provides the state and methods common to {@link Bytecodes#LOOKUPSWITCH} + * and {@link Bytecodes#TABLESWITCH} instructions. + */ +public abstract class BytecodeSwitch { + /** + * The {@link BytecodeStream} containing bytecode array or {@code null} if {@link #code} is not {@code null}. + */ + private final BytecodeStream stream; + /** + * The bytecode array or {@code null} if {@link #stream} is not {@code null}. + */ + private final byte[] code; + /** + * Index of start of switch instruction. + */ + protected final int bci; + /** + * Index of the start of the additional data for the switch instruction, aligned to a multiple of four from the method start. + */ + protected final int alignedBci; + + /** + * Constructor for a {@link BytecodeStream}. + * @param stream the {@code BytecodeStream} containing the switch instruction + * @param bci the index in the stream of the switch instruction + */ + public BytecodeSwitch(BytecodeStream stream, int bci) { + this.alignedBci = (bci + 4) & 0xfffffffc; + this.stream = stream; + this.code = null; + this.bci = bci; + } + + /** + * Constructor for a bytecode array. + * @param code the bytecode array containing the switch instruction. + * @param bci the index in the array of the switch instruction + */ + public BytecodeSwitch(byte[] code, int bci) { + this.alignedBci = (bci + 4) & 0xfffffffc; + this.stream = null; + this.code = code; + this.bci = bci; + } + + /** + * Gets the current bytecode index. + * @return the current bytecode index + */ + public int bci() { + return bci; + } + + /** + * Gets the index of the instruction denoted by the {@code i}'th switch target. + * @param i index of the switch target + * @return the index of the instruction denoted by the {@code i}'th switch target + */ + public int targetAt(int i) { + return bci + offsetAt(i); + } + + /** + * Gets the index of the instruction for the default switch target. + * @return the index of the instruction for the default switch target + */ + public int defaultTarget() { + return bci + defaultOffset(); + } + + /** + * Gets the offset from the start of the switch instruction to the default switch target. + * @return the offset to the default switch target + */ + public abstract int defaultOffset(); + + /** + * Gets the key at {@code i}'th switch target index. + * @param i the switch target index + * @return the key at {@code i}'th switch target index + */ + public abstract int keyAt(int i); + + /** + * Gets the offset from the start of the switch instruction for the {@code i}'th switch target. + * @param i the switch target index + * @return the offset to the {@code i}'th switch target + */ + public abstract int offsetAt(int i); + + /** + * Gets the number of switch targets. + * @return the number of switch targets + */ + public abstract int numberOfCases(); + + /** + * Gets the total size in bytes of the switch instruction. + * @return the total size in bytes of the switch instruction + */ + public abstract int size(); + + /** + * Reads the signed value at given bytecode index. + * @param readBci the start index of the value to retrieve + * @return the signed, 4-byte value in the bytecode array starting at {@code bci} + */ + protected int readWord(int readBci) { + if (code != null) { + return Bytes.beS4(code, readBci); + } + return stream.readInt(readBci); + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeTableSwitch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeTableSwitch.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +/** + * A utility for processing {@link Bytecodes#TABLESWITCH} bytecodes. + */ +public class BytecodeTableSwitch extends BytecodeSwitch { + private static final int OFFSET_TO_LOW_KEY = 4; + private static final int OFFSET_TO_HIGH_KEY = 8; + private static final int OFFSET_TO_FIRST_JUMP_OFFSET = 12; + private static final int JUMP_OFFSET_SIZE = 4; + + /** + * Constructor for a {@link BytecodeStream}. + * @param stream the {@code BytecodeStream} containing the switch instruction + * @param bci the index in the stream of the switch instruction + */ + public BytecodeTableSwitch(BytecodeStream stream, int bci) { + super(stream, bci); + } + + /** + * Constructor for a bytecode array. + * @param code the bytecode array containing the switch instruction. + * @param bci the index in the array of the switch instruction + */ + public BytecodeTableSwitch(byte[] code, int bci) { + super(code, bci); + } + + /** + * Gets the low key of the table switch. + * @return the low key + */ + public int lowKey() { + return readWord(alignedBci + OFFSET_TO_LOW_KEY); + } + + /** + * Gets the high key of the table switch. + * @return the high key + */ + public int highKey() { + return readWord(alignedBci + OFFSET_TO_HIGH_KEY); + } + + @Override + public int keyAt(int i) { + return lowKey() + i; + } + + @Override + public int defaultOffset() { + return readWord(alignedBci); + } + + @Override + public int offsetAt(int i) { + return readWord(alignedBci + OFFSET_TO_FIRST_JUMP_OFFSET + JUMP_OFFSET_SIZE * i); + } + + @Override + public int numberOfCases() { + return highKey() - lowKey() + 1; + } + + @Override + public int size() { + return alignedBci + OFFSET_TO_FIRST_JUMP_OFFSET + JUMP_OFFSET_SIZE * numberOfCases() - bci; + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytecodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytecodes.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,961 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import static com.oracle.max.graal.java.Bytecodes.Flags.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.regex.*; + +/** + * The definitions of the bytecodes that are valid input to the compiler and + * related utility methods. This comprises two groups: the standard Java + * bytecodes defined by + * Java Virtual Machine Specification, and a set of extended + * bytecodes that support low-level programming, for example, memory barriers. + * + * The extended bytecodes are one or three bytes in size. The one-byte bytecodes + * follow the values in the standard set, with no gap. The three-byte extended + * bytecodes share a common first byte and carry additional instruction-specific + * information in the second and third bytes. + */ +public class Bytecodes { + public static final int NOP = 0; // 0x00 + public static final int ACONST_NULL = 1; // 0x01 + public static final int ICONST_M1 = 2; // 0x02 + public static final int ICONST_0 = 3; // 0x03 + public static final int ICONST_1 = 4; // 0x04 + public static final int ICONST_2 = 5; // 0x05 + public static final int ICONST_3 = 6; // 0x06 + public static final int ICONST_4 = 7; // 0x07 + public static final int ICONST_5 = 8; // 0x08 + public static final int LCONST_0 = 9; // 0x09 + public static final int LCONST_1 = 10; // 0x0A + public static final int FCONST_0 = 11; // 0x0B + public static final int FCONST_1 = 12; // 0x0C + public static final int FCONST_2 = 13; // 0x0D + public static final int DCONST_0 = 14; // 0x0E + public static final int DCONST_1 = 15; // 0x0F + public static final int BIPUSH = 16; // 0x10 + public static final int SIPUSH = 17; // 0x11 + public static final int LDC = 18; // 0x12 + public static final int LDC_W = 19; // 0x13 + public static final int LDC2_W = 20; // 0x14 + public static final int ILOAD = 21; // 0x15 + public static final int LLOAD = 22; // 0x16 + public static final int FLOAD = 23; // 0x17 + public static final int DLOAD = 24; // 0x18 + public static final int ALOAD = 25; // 0x19 + public static final int ILOAD_0 = 26; // 0x1A + public static final int ILOAD_1 = 27; // 0x1B + public static final int ILOAD_2 = 28; // 0x1C + public static final int ILOAD_3 = 29; // 0x1D + public static final int LLOAD_0 = 30; // 0x1E + public static final int LLOAD_1 = 31; // 0x1F + public static final int LLOAD_2 = 32; // 0x20 + public static final int LLOAD_3 = 33; // 0x21 + public static final int FLOAD_0 = 34; // 0x22 + public static final int FLOAD_1 = 35; // 0x23 + public static final int FLOAD_2 = 36; // 0x24 + public static final int FLOAD_3 = 37; // 0x25 + public static final int DLOAD_0 = 38; // 0x26 + public static final int DLOAD_1 = 39; // 0x27 + public static final int DLOAD_2 = 40; // 0x28 + public static final int DLOAD_3 = 41; // 0x29 + public static final int ALOAD_0 = 42; // 0x2A + public static final int ALOAD_1 = 43; // 0x2B + public static final int ALOAD_2 = 44; // 0x2C + public static final int ALOAD_3 = 45; // 0x2D + public static final int IALOAD = 46; // 0x2E + public static final int LALOAD = 47; // 0x2F + public static final int FALOAD = 48; // 0x30 + public static final int DALOAD = 49; // 0x31 + public static final int AALOAD = 50; // 0x32 + public static final int BALOAD = 51; // 0x33 + public static final int CALOAD = 52; // 0x34 + public static final int SALOAD = 53; // 0x35 + public static final int ISTORE = 54; // 0x36 + public static final int LSTORE = 55; // 0x37 + public static final int FSTORE = 56; // 0x38 + public static final int DSTORE = 57; // 0x39 + public static final int ASTORE = 58; // 0x3A + public static final int ISTORE_0 = 59; // 0x3B + public static final int ISTORE_1 = 60; // 0x3C + public static final int ISTORE_2 = 61; // 0x3D + public static final int ISTORE_3 = 62; // 0x3E + public static final int LSTORE_0 = 63; // 0x3F + public static final int LSTORE_1 = 64; // 0x40 + public static final int LSTORE_2 = 65; // 0x41 + public static final int LSTORE_3 = 66; // 0x42 + public static final int FSTORE_0 = 67; // 0x43 + public static final int FSTORE_1 = 68; // 0x44 + public static final int FSTORE_2 = 69; // 0x45 + public static final int FSTORE_3 = 70; // 0x46 + public static final int DSTORE_0 = 71; // 0x47 + public static final int DSTORE_1 = 72; // 0x48 + public static final int DSTORE_2 = 73; // 0x49 + public static final int DSTORE_3 = 74; // 0x4A + public static final int ASTORE_0 = 75; // 0x4B + public static final int ASTORE_1 = 76; // 0x4C + public static final int ASTORE_2 = 77; // 0x4D + public static final int ASTORE_3 = 78; // 0x4E + public static final int IASTORE = 79; // 0x4F + public static final int LASTORE = 80; // 0x50 + public static final int FASTORE = 81; // 0x51 + public static final int DASTORE = 82; // 0x52 + public static final int AASTORE = 83; // 0x53 + public static final int BASTORE = 84; // 0x54 + public static final int CASTORE = 85; // 0x55 + public static final int SASTORE = 86; // 0x56 + public static final int POP = 87; // 0x57 + public static final int POP2 = 88; // 0x58 + public static final int DUP = 89; // 0x59 + public static final int DUP_X1 = 90; // 0x5A + public static final int DUP_X2 = 91; // 0x5B + public static final int DUP2 = 92; // 0x5C + public static final int DUP2_X1 = 93; // 0x5D + public static final int DUP2_X2 = 94; // 0x5E + public static final int SWAP = 95; // 0x5F + public static final int IADD = 96; // 0x60 + public static final int LADD = 97; // 0x61 + public static final int FADD = 98; // 0x62 + public static final int DADD = 99; // 0x63 + public static final int ISUB = 100; // 0x64 + public static final int LSUB = 101; // 0x65 + public static final int FSUB = 102; // 0x66 + public static final int DSUB = 103; // 0x67 + public static final int IMUL = 104; // 0x68 + public static final int LMUL = 105; // 0x69 + public static final int FMUL = 106; // 0x6A + public static final int DMUL = 107; // 0x6B + public static final int IDIV = 108; // 0x6C + public static final int LDIV = 109; // 0x6D + public static final int FDIV = 110; // 0x6E + public static final int DDIV = 111; // 0x6F + public static final int IREM = 112; // 0x70 + public static final int LREM = 113; // 0x71 + public static final int FREM = 114; // 0x72 + public static final int DREM = 115; // 0x73 + public static final int INEG = 116; // 0x74 + public static final int LNEG = 117; // 0x75 + public static final int FNEG = 118; // 0x76 + public static final int DNEG = 119; // 0x77 + public static final int ISHL = 120; // 0x78 + public static final int LSHL = 121; // 0x79 + public static final int ISHR = 122; // 0x7A + public static final int LSHR = 123; // 0x7B + public static final int IUSHR = 124; // 0x7C + public static final int LUSHR = 125; // 0x7D + public static final int IAND = 126; // 0x7E + public static final int LAND = 127; // 0x7F + public static final int IOR = 128; // 0x80 + public static final int LOR = 129; // 0x81 + public static final int IXOR = 130; // 0x82 + public static final int LXOR = 131; // 0x83 + public static final int IINC = 132; // 0x84 + public static final int I2L = 133; // 0x85 + public static final int I2F = 134; // 0x86 + public static final int I2D = 135; // 0x87 + public static final int L2I = 136; // 0x88 + public static final int L2F = 137; // 0x89 + public static final int L2D = 138; // 0x8A + public static final int F2I = 139; // 0x8B + public static final int F2L = 140; // 0x8C + public static final int F2D = 141; // 0x8D + public static final int D2I = 142; // 0x8E + public static final int D2L = 143; // 0x8F + public static final int D2F = 144; // 0x90 + public static final int I2B = 145; // 0x91 + public static final int I2C = 146; // 0x92 + public static final int I2S = 147; // 0x93 + public static final int LCMP = 148; // 0x94 + public static final int FCMPL = 149; // 0x95 + public static final int FCMPG = 150; // 0x96 + public static final int DCMPL = 151; // 0x97 + public static final int DCMPG = 152; // 0x98 + public static final int IFEQ = 153; // 0x99 + public static final int IFNE = 154; // 0x9A + public static final int IFLT = 155; // 0x9B + public static final int IFGE = 156; // 0x9C + public static final int IFGT = 157; // 0x9D + public static final int IFLE = 158; // 0x9E + public static final int IF_ICMPEQ = 159; // 0x9F + public static final int IF_ICMPNE = 160; // 0xA0 + public static final int IF_ICMPLT = 161; // 0xA1 + public static final int IF_ICMPGE = 162; // 0xA2 + public static final int IF_ICMPGT = 163; // 0xA3 + public static final int IF_ICMPLE = 164; // 0xA4 + public static final int IF_ACMPEQ = 165; // 0xA5 + public static final int IF_ACMPNE = 166; // 0xA6 + public static final int GOTO = 167; // 0xA7 + public static final int JSR = 168; // 0xA8 + public static final int RET = 169; // 0xA9 + public static final int TABLESWITCH = 170; // 0xAA + public static final int LOOKUPSWITCH = 171; // 0xAB + public static final int IRETURN = 172; // 0xAC + public static final int LRETURN = 173; // 0xAD + public static final int FRETURN = 174; // 0xAE + public static final int DRETURN = 175; // 0xAF + public static final int ARETURN = 176; // 0xB0 + public static final int RETURN = 177; // 0xB1 + public static final int GETSTATIC = 178; // 0xB2 + public static final int PUTSTATIC = 179; // 0xB3 + public static final int GETFIELD = 180; // 0xB4 + public static final int PUTFIELD = 181; // 0xB5 + public static final int INVOKEVIRTUAL = 182; // 0xB6 + public static final int INVOKESPECIAL = 183; // 0xB7 + public static final int INVOKESTATIC = 184; // 0xB8 + public static final int INVOKEINTERFACE = 185; // 0xB9 + public static final int XXXUNUSEDXXX = 186; // 0xBA + public static final int NEW = 187; // 0xBB + public static final int NEWARRAY = 188; // 0xBC + public static final int ANEWARRAY = 189; // 0xBD + public static final int ARRAYLENGTH = 190; // 0xBE + public static final int ATHROW = 191; // 0xBF + public static final int CHECKCAST = 192; // 0xC0 + public static final int INSTANCEOF = 193; // 0xC1 + public static final int MONITORENTER = 194; // 0xC2 + public static final int MONITOREXIT = 195; // 0xC3 + public static final int WIDE = 196; // 0xC4 + public static final int MULTIANEWARRAY = 197; // 0xC5 + public static final int IFNULL = 198; // 0xC6 + public static final int IFNONNULL = 199; // 0xC7 + public static final int GOTO_W = 200; // 0xC8 + public static final int JSR_W = 201; // 0xC9 + public static final int BREAKPOINT = 202; // 0xCA + + public static final int ILLEGAL = 255; + public static final int END = 256; + + /** + * The last opcode defined by the JVM specification. To iterate over all JVM bytecodes: + *

+     *     for (int opcode = 0; opcode <= Bytecodes.LAST_JVM_OPCODE; ++opcode) {
+     *         //
+     *     }
+     * 
+ */ + public static final int LAST_JVM_OPCODE = JSR_W; + + /** + * A collection of flags describing various bytecode attributes. + */ + static class Flags { + + /** + * Denotes an instruction that ends a basic block and does not let control flow fall through to its lexical successor. + */ + static final int STOP = 0x00000001; + + /** + * Denotes an instruction that ends a basic block and may let control flow fall through to its lexical successor. + * In practice this means it is a conditional branch. + */ + static final int FALL_THROUGH = 0x00000002; + + /** + * Denotes an instruction that has a 2 or 4 byte operand that is an offset to another instruction in the same method. + * This does not include the {@link Bytecodes#TABLESWITCH} or {@link Bytecodes#LOOKUPSWITCH} instructions. + */ + static final int BRANCH = 0x00000004; + + /** + * Denotes an instruction that reads the value of a static or instance field. + */ + static final int FIELD_READ = 0x00000008; + + /** + * Denotes an instruction that writes the value of a static or instance field. + */ + static final int FIELD_WRITE = 0x00000010; + + /** + * Denotes an instruction that is not defined in the JVM specification. + */ + static final int EXTENSION = 0x00000020; + + /** + * Denotes an instruction that can cause a trap. + */ + static final int TRAP = 0x00000080; + /** + * Denotes an instruction that is commutative. + */ + static final int COMMUTATIVE = 0x00000100; + /** + * Denotes an instruction that is associative. + */ + static final int ASSOCIATIVE = 0x00000200; + /** + * Denotes an instruction that loads an operand. + */ + static final int LOAD = 0x00000400; + /** + * Denotes an instruction that stores an operand. + */ + static final int STORE = 0x00000800; + /** + * Denotes the 4 INVOKE* instructions. + */ + static final int INVOKE = 0x00001000; + } + + // Performs a sanity check that none of the flags overlap. + static { + int allFlags = 0; + try { + for (Field field : Flags.class.getDeclaredFields()) { + int flagsFilter = Modifier.FINAL | Modifier.STATIC; + if ((field.getModifiers() & flagsFilter) == flagsFilter) { + assert field.getType() == int.class : "Only " + field; + final int flag = field.getInt(null); + assert flag != 0; + assert (flag & allFlags) == 0 : field.getName() + " has a value conflicting with another flag"; + allFlags |= flag; + } + } + } catch (Exception e) { + throw new InternalError(e.toString()); + } + } + + /** + * A array that maps from a bytecode value to a {@link String} for the corresponding instruction mnemonic. + * This will include the root instruction for the three-byte extended instructions. + */ + private static final String[] nameArray = new String[256]; + + /** + * A array that maps from a bytecode value to the set of {@link Flags} for the corresponding instruction. + */ + private static final int[] flagsArray = new int[256]; + + /** + * A array that maps from a bytecode value to the length in bytes for the corresponding instruction. + */ + private static final int[] lengthArray = new int[256]; + + // Checkstyle: stop + static { + def(NOP , "nop" , "b" ); + def(ACONST_NULL , "aconst_null" , "b" ); + def(ICONST_M1 , "iconst_m1" , "b" ); + def(ICONST_0 , "iconst_0" , "b" ); + def(ICONST_1 , "iconst_1" , "b" ); + def(ICONST_2 , "iconst_2" , "b" ); + def(ICONST_3 , "iconst_3" , "b" ); + def(ICONST_4 , "iconst_4" , "b" ); + def(ICONST_5 , "iconst_5" , "b" ); + def(LCONST_0 , "lconst_0" , "b" ); + def(LCONST_1 , "lconst_1" , "b" ); + def(FCONST_0 , "fconst_0" , "b" ); + def(FCONST_1 , "fconst_1" , "b" ); + def(FCONST_2 , "fconst_2" , "b" ); + def(DCONST_0 , "dconst_0" , "b" ); + def(DCONST_1 , "dconst_1" , "b" ); + def(BIPUSH , "bipush" , "bc" ); + def(SIPUSH , "sipush" , "bcc" ); + def(LDC , "ldc" , "bi" , TRAP); + def(LDC_W , "ldc_w" , "bii" , TRAP); + def(LDC2_W , "ldc2_w" , "bii" , TRAP); + def(ILOAD , "iload" , "bi" , LOAD); + def(LLOAD , "lload" , "bi" , LOAD); + def(FLOAD , "fload" , "bi" , LOAD); + def(DLOAD , "dload" , "bi" , LOAD); + def(ALOAD , "aload" , "bi" , LOAD); + def(ILOAD_0 , "iload_0" , "b" , LOAD); + def(ILOAD_1 , "iload_1" , "b" , LOAD); + def(ILOAD_2 , "iload_2" , "b" , LOAD); + def(ILOAD_3 , "iload_3" , "b" , LOAD); + def(LLOAD_0 , "lload_0" , "b" , LOAD); + def(LLOAD_1 , "lload_1" , "b" , LOAD); + def(LLOAD_2 , "lload_2" , "b" , LOAD); + def(LLOAD_3 , "lload_3" , "b" , LOAD); + def(FLOAD_0 , "fload_0" , "b" , LOAD); + def(FLOAD_1 , "fload_1" , "b" , LOAD); + def(FLOAD_2 , "fload_2" , "b" , LOAD); + def(FLOAD_3 , "fload_3" , "b" , LOAD); + def(DLOAD_0 , "dload_0" , "b" , LOAD); + def(DLOAD_1 , "dload_1" , "b" , LOAD); + def(DLOAD_2 , "dload_2" , "b" , LOAD); + def(DLOAD_3 , "dload_3" , "b" , LOAD); + def(ALOAD_0 , "aload_0" , "b" , LOAD); + def(ALOAD_1 , "aload_1" , "b" , LOAD); + def(ALOAD_2 , "aload_2" , "b" , LOAD); + def(ALOAD_3 , "aload_3" , "b" , LOAD); + def(IALOAD , "iaload" , "b" , TRAP); + def(LALOAD , "laload" , "b" , TRAP); + def(FALOAD , "faload" , "b" , TRAP); + def(DALOAD , "daload" , "b" , TRAP); + def(AALOAD , "aaload" , "b" , TRAP); + def(BALOAD , "baload" , "b" , TRAP); + def(CALOAD , "caload" , "b" , TRAP); + def(SALOAD , "saload" , "b" , TRAP); + def(ISTORE , "istore" , "bi" , STORE); + def(LSTORE , "lstore" , "bi" , STORE); + def(FSTORE , "fstore" , "bi" , STORE); + def(DSTORE , "dstore" , "bi" , STORE); + def(ASTORE , "astore" , "bi" , STORE); + def(ISTORE_0 , "istore_0" , "b" , STORE); + def(ISTORE_1 , "istore_1" , "b" , STORE); + def(ISTORE_2 , "istore_2" , "b" , STORE); + def(ISTORE_3 , "istore_3" , "b" , STORE); + def(LSTORE_0 , "lstore_0" , "b" , STORE); + def(LSTORE_1 , "lstore_1" , "b" , STORE); + def(LSTORE_2 , "lstore_2" , "b" , STORE); + def(LSTORE_3 , "lstore_3" , "b" , STORE); + def(FSTORE_0 , "fstore_0" , "b" , STORE); + def(FSTORE_1 , "fstore_1" , "b" , STORE); + def(FSTORE_2 , "fstore_2" , "b" , STORE); + def(FSTORE_3 , "fstore_3" , "b" , STORE); + def(DSTORE_0 , "dstore_0" , "b" , STORE); + def(DSTORE_1 , "dstore_1" , "b" , STORE); + def(DSTORE_2 , "dstore_2" , "b" , STORE); + def(DSTORE_3 , "dstore_3" , "b" , STORE); + def(ASTORE_0 , "astore_0" , "b" , STORE); + def(ASTORE_1 , "astore_1" , "b" , STORE); + def(ASTORE_2 , "astore_2" , "b" , STORE); + def(ASTORE_3 , "astore_3" , "b" , STORE); + def(IASTORE , "iastore" , "b" , TRAP); + def(LASTORE , "lastore" , "b" , TRAP); + def(FASTORE , "fastore" , "b" , TRAP); + def(DASTORE , "dastore" , "b" , TRAP); + def(AASTORE , "aastore" , "b" , TRAP); + def(BASTORE , "bastore" , "b" , TRAP); + def(CASTORE , "castore" , "b" , TRAP); + def(SASTORE , "sastore" , "b" , TRAP); + def(POP , "pop" , "b" ); + def(POP2 , "pop2" , "b" ); + def(DUP , "dup" , "b" ); + def(DUP_X1 , "dup_x1" , "b" ); + def(DUP_X2 , "dup_x2" , "b" ); + def(DUP2 , "dup2" , "b" ); + def(DUP2_X1 , "dup2_x1" , "b" ); + def(DUP2_X2 , "dup2_x2" , "b" ); + def(SWAP , "swap" , "b" ); + def(IADD , "iadd" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(LADD , "ladd" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(FADD , "fadd" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(DADD , "dadd" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(ISUB , "isub" , "b" ); + def(LSUB , "lsub" , "b" ); + def(FSUB , "fsub" , "b" ); + def(DSUB , "dsub" , "b" ); + def(IMUL , "imul" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(LMUL , "lmul" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(FMUL , "fmul" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(DMUL , "dmul" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(IDIV , "idiv" , "b" , TRAP); + def(LDIV , "ldiv" , "b" , TRAP); + def(FDIV , "fdiv" , "b" ); + def(DDIV , "ddiv" , "b" ); + def(IREM , "irem" , "b" , TRAP); + def(LREM , "lrem" , "b" , TRAP); + def(FREM , "frem" , "b" ); + def(DREM , "drem" , "b" ); + def(INEG , "ineg" , "b" ); + def(LNEG , "lneg" , "b" ); + def(FNEG , "fneg" , "b" ); + def(DNEG , "dneg" , "b" ); + def(ISHL , "ishl" , "b" ); + def(LSHL , "lshl" , "b" ); + def(ISHR , "ishr" , "b" ); + def(LSHR , "lshr" , "b" ); + def(IUSHR , "iushr" , "b" ); + def(LUSHR , "lushr" , "b" ); + def(IAND , "iand" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(LAND , "land" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(IOR , "ior" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(LOR , "lor" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(IXOR , "ixor" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(LXOR , "lxor" , "b" , COMMUTATIVE | ASSOCIATIVE); + def(IINC , "iinc" , "bic" , LOAD | STORE); + def(I2L , "i2l" , "b" ); + def(I2F , "i2f" , "b" ); + def(I2D , "i2d" , "b" ); + def(L2I , "l2i" , "b" ); + def(L2F , "l2f" , "b" ); + def(L2D , "l2d" , "b" ); + def(F2I , "f2i" , "b" ); + def(F2L , "f2l" , "b" ); + def(F2D , "f2d" , "b" ); + def(D2I , "d2i" , "b" ); + def(D2L , "d2l" , "b" ); + def(D2F , "d2f" , "b" ); + def(I2B , "i2b" , "b" ); + def(I2C , "i2c" , "b" ); + def(I2S , "i2s" , "b" ); + def(LCMP , "lcmp" , "b" ); + def(FCMPL , "fcmpl" , "b" ); + def(FCMPG , "fcmpg" , "b" ); + def(DCMPL , "dcmpl" , "b" ); + def(DCMPG , "dcmpg" , "b" ); + def(IFEQ , "ifeq" , "boo" , FALL_THROUGH | BRANCH); + def(IFNE , "ifne" , "boo" , FALL_THROUGH | BRANCH); + def(IFLT , "iflt" , "boo" , FALL_THROUGH | BRANCH); + def(IFGE , "ifge" , "boo" , FALL_THROUGH | BRANCH); + def(IFGT , "ifgt" , "boo" , FALL_THROUGH | BRANCH); + def(IFLE , "ifle" , "boo" , FALL_THROUGH | BRANCH); + def(IF_ICMPEQ , "if_icmpeq" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ICMPNE , "if_icmpne" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ICMPLT , "if_icmplt" , "boo" , FALL_THROUGH | BRANCH); + def(IF_ICMPGE , "if_icmpge" , "boo" , FALL_THROUGH | BRANCH); + def(IF_ICMPGT , "if_icmpgt" , "boo" , FALL_THROUGH | BRANCH); + def(IF_ICMPLE , "if_icmple" , "boo" , FALL_THROUGH | BRANCH); + def(IF_ACMPEQ , "if_acmpeq" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ACMPNE , "if_acmpne" , "boo" , COMMUTATIVE | FALL_THROUGH | BRANCH); + def(GOTO , "goto" , "boo" , STOP | BRANCH); + def(JSR , "jsr" , "boo" , STOP | BRANCH); + def(RET , "ret" , "bi" , STOP); + def(TABLESWITCH , "tableswitch" , "" , STOP); + def(LOOKUPSWITCH , "lookupswitch" , "" , STOP); + def(IRETURN , "ireturn" , "b" , TRAP | STOP); + def(LRETURN , "lreturn" , "b" , TRAP | STOP); + def(FRETURN , "freturn" , "b" , TRAP | STOP); + def(DRETURN , "dreturn" , "b" , TRAP | STOP); + def(ARETURN , "areturn" , "b" , TRAP | STOP); + def(RETURN , "return" , "b" , TRAP | STOP); + def(GETSTATIC , "getstatic" , "bjj" , TRAP | FIELD_READ); + def(PUTSTATIC , "putstatic" , "bjj" , TRAP | FIELD_WRITE); + def(GETFIELD , "getfield" , "bjj" , TRAP | FIELD_READ); + def(PUTFIELD , "putfield" , "bjj" , TRAP | FIELD_WRITE); + def(INVOKEVIRTUAL , "invokevirtual" , "bjj" , TRAP | INVOKE); + def(INVOKESPECIAL , "invokespecial" , "bjj" , TRAP | INVOKE); + def(INVOKESTATIC , "invokestatic" , "bjj" , TRAP | INVOKE); + def(INVOKEINTERFACE , "invokeinterface" , "bjja_", TRAP | INVOKE); + def(XXXUNUSEDXXX , "xxxunusedxxx" , "" ); + def(NEW , "new" , "bii" , TRAP); + def(NEWARRAY , "newarray" , "bc" , TRAP); + def(ANEWARRAY , "anewarray" , "bii" , TRAP); + def(ARRAYLENGTH , "arraylength" , "b" , TRAP); + def(ATHROW , "athrow" , "b" , TRAP | STOP); + def(CHECKCAST , "checkcast" , "bii" , TRAP); + def(INSTANCEOF , "instanceof" , "bii" , TRAP); + def(MONITORENTER , "monitorenter" , "b" , TRAP); + def(MONITOREXIT , "monitorexit" , "b" , TRAP); + def(WIDE , "wide" , "" ); + def(MULTIANEWARRAY , "multianewarray" , "biic" , TRAP); + def(IFNULL , "ifnull" , "boo" , FALL_THROUGH | BRANCH); + def(IFNONNULL , "ifnonnull" , "boo" , FALL_THROUGH | BRANCH); + def(GOTO_W , "goto_w" , "boooo", STOP | BRANCH); + def(JSR_W , "jsr_w" , "boooo", STOP | BRANCH); + def(BREAKPOINT , "breakpoint" , "b" , TRAP); + } + // Checkstyle: resume + + /** + * Determines if an opcode is commutative. + * @param opcode the opcode to check + * @return {@code true} iff commutative + */ + public static boolean isCommutative(int opcode) { + return (flagsArray[opcode & 0xff] & COMMUTATIVE) != 0; + } + + /** + * Gets the length of an instruction denoted by a given opcode. + * + * @param opcode an instruction opcode + * @return the length of the instruction denoted by {@code opcode}. If {@code opcode} is an illegal instruction or denotes a + * variable length instruction (e.g. {@link #TABLESWITCH}), then 0 is returned. + */ + public static int lengthOf(int opcode) { + return lengthArray[opcode & 0xff]; + } + + /** + * Gets the length of an instruction at a given position in a given bytecode array. + * This methods handles variable length and {@linkplain #WIDE widened} instructions. + * + * @param code an array of bytecode + * @param bci the position in {@code code} of an instruction's opcode + * @return the length of the instruction at position {@code bci} in {@code code} + */ + public static int lengthOf(byte[] code, int bci) { + int opcode = Bytes.beU1(code, bci); + int length = Bytecodes.lengthArray[opcode & 0xff]; + if (length == 0) { + switch (opcode) { + case TABLESWITCH: { + return new BytecodeTableSwitch(code, bci).size(); + } + case LOOKUPSWITCH: { + return new BytecodeLookupSwitch(code, bci).size(); + } + case WIDE: { + int opc = Bytes.beU1(code, bci + 1); + if (opc == RET) { + return 4; + } else if (opc == IINC) { + return 6; + } else { + return 4; // a load or store bytecode + } + } + default: + throw new Error("unknown variable-length bytecode: " + opcode); + } + } + return length; + } + + /** + * Gets the lower-case mnemonic for a given opcode. + * + * @param opcode an opcode + * @return the mnemonic for {@code opcode} or {@code ""} if {@code opcode} is not a legal opcode + */ + public static String nameOf(int opcode) throws IllegalArgumentException { + String name = nameArray[opcode & 0xff]; + if (name == null) { + return ""; + } + return name; + } + + /** + * Allocation-free version of {@linkplain #nameOf(int)}. + * @param opcode an opcode. + * @return the mnemonic for {@code opcode} or {@code ""} if {@code opcode} is not a legal opcode. + */ + public static String baseNameOf(int opcode) { + String name = nameArray[opcode & 0xff]; + if (name == null) { + return ""; + } + return name; + } + + /** + * Gets the opcode corresponding to a given mnemonic. + * + * @param name an opcode mnemonic + * @return the opcode corresponding to {@code mnemonic} + * @throws IllegalArgumentException if {@code name} does not denote a valid opcode + */ + public static int valueOf(String name) { + for (int opcode = 0; opcode < nameArray.length; ++opcode) { + if (name.equalsIgnoreCase(nameArray[opcode])) { + return opcode; + } + } + throw new IllegalArgumentException("No opcode for " + name); + } + + /** + * Determines if a given opcode denotes an instruction that can cause an implicit exception. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} can cause an implicit exception, {@code false} otherwise + */ + public static boolean canTrap(int opcode) { + return (flagsArray[opcode & 0xff] & TRAP) != 0; + } + + /** + * Determines if a given opcode denotes an instruction that loads a local variable to the operand stack. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} loads a local variable to the operand stack, {@code false} otherwise + */ + public static boolean isLoad(int opcode) { + return (flagsArray[opcode & 0xff] & LOAD) != 0; + } + + /** + * Determines if a given opcode denotes an instruction that ends a basic block and does not let control flow fall + * through to its lexical successor. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} properly ends a basic block + */ + public static boolean isStop(int opcode) { + return (flagsArray[opcode & 0xff] & STOP) != 0; + } + + /** + * Determines if a given opcode denotes an instruction that stores a value to a local variable + * after popping it from the operand stack. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} stores a value to a local variable, {@code false} otherwise + */ + public static boolean isInvoke(int opcode) { + return (flagsArray[opcode & 0xff] & INVOKE) != 0; + } + + /** + * Determines if a given opcode denotes an instruction that stores a value to a local variable + * after popping it from the operand stack. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} stores a value to a local variable, {@code false} otherwise + */ + public static boolean isStore(int opcode) { + return (flagsArray[opcode & 0xff] & STORE) != 0; + } + + /** + * Determines if a given opcode is an instruction that delimits a basic block. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} delimits a basic block + */ + public static boolean isBlockEnd(int opcode) { + return (flagsArray[opcode & 0xff] & (STOP | FALL_THROUGH)) != 0; + } + + /** + * Determines if a given opcode is an instruction that has a 2 or 4 byte operand that is an offset to another + * instruction in the same method. This does not include the {@linkplain #TABLESWITCH switch} instructions. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} is a branch instruction with a single operand + */ + public static boolean isBranch(int opcode) { + return (flagsArray[opcode & 0xff] & BRANCH) != 0; + } + + /** + * Determines if a given opcode denotes a conditional branch. + * @param opcode + * @return {@code true} iff {@code opcode} is a conditional branch + */ + public static boolean isConditionalBranch(int opcode) { + return (flagsArray[opcode & 0xff] & FALL_THROUGH) != 0; + } + + /** + * Determines if a given opcode denotes a standard bytecode. A standard bytecode is + * defined in the JVM specification. + * + * @param opcode an opcode to test + * @return {@code true} iff {@code opcode} is a standard bytecode + */ + public static boolean isStandard(int opcode) { + return (flagsArray[opcode & 0xff] & EXTENSION) == 0; + } + + /** + * Determines if a given opcode denotes an extended bytecode. + * + * @param opcode an opcode to test + * @return {@code true} if {@code opcode} is an extended bytecode + */ + public static boolean isExtended(int opcode) { + return (flagsArray[opcode & 0xff] & EXTENSION) != 0; + } + + /** + * Determines if a given opcode is a three-byte extended bytecode. + * + * @param opcode an opcode to test + * @return {@code true} if {@code (opcode & ~0xff) != 0} + */ + public static boolean isThreeByteExtended(int opcode) { + return (opcode & ~0xff) != 0; + } + + /** + * Gets the arithmetic operator name for a given opcode. If {@code opcode} does not denote an + * arithmetic instruction, then the {@linkplain #nameOf(int) name} of the opcode is returned + * instead. + * + * @param op an opcode + * @return the arithmetic operator name + */ + public static String operator(int op) { + // Checkstyle: stop + switch (op) { + // arithmetic ops + case IADD : // fall through + case LADD : // fall through + case FADD : // fall through + case DADD : return "+"; + case ISUB : // fall through + case LSUB : // fall through + case FSUB : // fall through + case DSUB : return "-"; + case IMUL : // fall through + case LMUL : // fall through + case FMUL : // fall through + case DMUL : return "*"; + case IDIV : // fall through + case LDIV : // fall through + case FDIV : // fall through + case DDIV : return "/"; + case IREM : // fall through + case LREM : // fall through + case FREM : // fall through + case DREM : return "%"; + // shift ops + case ISHL : // fall through + case LSHL : return "<<"; + case ISHR : // fall through + case LSHR : return ">>"; + case IUSHR: // fall through + case LUSHR: return ">>>"; + // logic ops + case IAND : // fall through + case LAND : return "&"; + case IOR : // fall through + case LOR : return "|"; + case IXOR : // fall through + case LXOR : return "^"; + } + // Checkstyle: resume + return nameOf(op); + } + + /** + * Defines a bytecode by entering it into the arrays that record its name, length and flags. + * + * @param name instruction name (should be lower case) + * @param format encodes the length of the instruction + * @param flagsArray the set of {@link Flags} associated with the instruction + */ + private static void def(int opcode, String name, String format) { + def(opcode, name, format, 0); + } + + /** + * Defines a bytecode by entering it into the arrays that record its name, length and flags. + * + * @param name instruction name (lower case) + * @param format encodes the length of the instruction + * @param flags the set of {@link Flags} associated with the instruction + */ + private static void def(int opcode, String name, String format, int flags) { + assert nameArray[opcode] == null : "opcode " + opcode + " is already bound to name " + nameArray[opcode]; + nameArray[opcode] = name; + int instructionLength = format.length(); + lengthArray[opcode] = instructionLength; + Bytecodes.flagsArray[opcode] = flags; + + assert !isConditionalBranch(opcode) || isBranch(opcode) : "a conditional branch must also be a branch"; + } + + /** + * Utility for ensuring that the extended opcodes are contiguous and follow on directly + * from the standard JVM opcodes. If these conditions do not hold for the input source + * file, then it is modified 'in situ' to fix the problem. + * + * @param args {@code args[0]} is the path to this source file + */ + public static void main(String[] args) throws Exception { + Method findWorkspaceDirectory = Class.forName("com.sun.max.ide.JavaProject").getDeclaredMethod("findWorkspaceDirectory"); + File base = new File((File) findWorkspaceDirectory.invoke(null), "com.oracle.max.cri/src"); + File file = new File(base, Bytecodes.class.getName().replace('.', File.separatorChar) + ".java").getAbsoluteFile(); + + Pattern opcodeDecl = Pattern.compile("(\\s*public static final int )(\\w+)(\\s*=\\s*)(\\d+)(;.*)"); + + BufferedReader br = new BufferedReader(new FileReader(file)); + CharArrayWriter buffer = new CharArrayWriter((int) file.length()); + PrintWriter out = new PrintWriter(buffer); + String line; + int lastExtendedOpcode = BREAKPOINT; + boolean modified = false; + int section = 0; + while ((line = br.readLine()) != null) { + if (section == 0) { + if (line.equals(" // Start extended bytecodes")) { + section = 1; + } + } else if (section == 1) { + if (line.equals(" // End extended bytecodes")) { + section = 2; + } else { + Matcher matcher = opcodeDecl.matcher(line); + if (matcher.matches()) { + String name = matcher.group(2); + String value = matcher.group(4); + int opcode = Integer.parseInt(value); + if (nameArray[opcode] == null || !nameArray[opcode].equalsIgnoreCase(name)) { + throw new RuntimeException("Missing definition of name and flags for " + opcode + ":" + name + " -- " + nameArray[opcode]); + } + if (opcode != lastExtendedOpcode + 1) { + System.err.println("Fixed declaration of opcode " + name + " to be " + (lastExtendedOpcode + 1) + " (was " + value + ")"); + opcode = lastExtendedOpcode + 1; + line = line.substring(0, matcher.start(4)) + opcode + line.substring(matcher.end(4)); + modified = true; + } + + if (opcode >= 256) { + throw new RuntimeException("Exceeded maximum opcode value with " + name); + } + + lastExtendedOpcode = opcode; + } + } + } + + out.println(line); + } + if (section == 0) { + throw new RuntimeException("Did not find line starting extended bytecode declarations:\n\n // Start extended bytecodes"); + } else if (section == 1) { + throw new RuntimeException("Did not find line ending extended bytecode declarations:\n\n // End extended bytecodes"); + } + + if (modified) { + out.flush(); + FileWriter fileWriter = new FileWriter(file); + fileWriter.write(buffer.toCharArray()); + fileWriter.close(); + + System.out.println("Modified: " + file); + } + + + // Uncomment to print out visitor method declarations: +// for (int opcode = 0; opcode < flags.length; ++opcode) { +// if (isExtension(opcode)) { +// String visitorParams = length(opcode) == 1 ? "" : "int index"; +// System.out.println("@Override"); +// System.out.println("protected void " + name(opcode) + "(" + visitorParams + ") {"); +// System.out.println("}"); +// System.out.println(); +// } +// } + + // Uncomment to print out visitor method declarations: +// for (int opcode = 0; opcode < flags.length; ++opcode) { +// if (isExtension(opcode)) { +// System.out.println("case " + name(opcode).toUpperCase() + ": {"); +// String arg = ""; +// int length = length(opcode); +// if (length == 2) { +// arg = "readUnsigned1()"; +// } else if (length == 3) { +// arg = "readUnsigned2()"; +// } +// System.out.println(" bytecodeVisitor." + name(opcode) + "(" + arg + ");"); +// System.out.println(" break;"); +// System.out.println("}"); +// } +// } + + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytes.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +/** + * A collection of utility methods for dealing with bytes, particularly in byte arrays. + */ +public class Bytes { + /** + * Gets a signed 1-byte value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @return the signed 1-byte value at index {@code bci} in array {@code data} + */ + public static int beS1(byte[] data, int bci) { + return data[bci]; + } + + /** + * Gets a signed 2-byte big-endian value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @return the signed 2-byte, big-endian, value at index {@code bci} in array {@code data} + */ + public static int beS2(byte[] data, int bci) { + return (data[bci] << 8) | (data[bci + 1] & 0xff); + } + + /** + * Gets an unsigned 1-byte value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @return the unsigned 1-byte value at index {@code bci} in array {@code data} + */ + public static int beU1(byte[] data, int bci) { + return data[bci] & 0xff; + } + + /** + * Gets an unsigned 2-byte big-endian value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @return the unsigned 2-byte, big-endian, value at index {@code bci} in array {@code data} + */ + public static int beU2(byte[] data, int bci) { + return ((data[bci] & 0xff) << 8) | (data[bci + 1] & 0xff); + } + + /** + * Gets a signed 4-byte big-endian value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @return the signed 4-byte, big-endian, value at index {@code bci} in array {@code data} + */ + public static int beS4(byte[] data, int bci) { + return (data[bci] << 24) | ((data[bci + 1] & 0xff) << 16) | ((data[bci + 2] & 0xff) << 8) | (data[bci + 3] & 0xff); + } + + /** + * Gets either a signed 2-byte or a signed 4-byte big-endian value. + * @param data the array containing the data + * @param bci the start index of the value to retrieve + * @param fourByte if true, this method will return a 4-byte value + * @return the signed 2 or 4-byte, big-endian, value at index {@code bci} in array {@code data} + */ + public static int beSVar(byte[] data, int bci, boolean fourByte) { + if (fourByte) { + return beS4(data, bci); + } else { + return beS2(data, bci); + } + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import static com.oracle.max.graal.nodes.ValueUtil.*; +import static java.lang.reflect.Modifier.*; + +import java.util.*; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.nodes.*; +import com.oracle.max.graal.nodes.PhiNode.PhiType; +import com.oracle.max.graal.nodes.spi.*; +import com.oracle.max.graal.nodes.type.*; + + +public class FrameStateBuilder implements FrameStateAccess { + + private final StructuredGraph graph; + + private final ValueNode[] locals; + private final ValueNode[] stack; + private final ArrayList locks; + + private int stackIndex; + private boolean rethrowException; + + private final RiResolvedMethod method; + + public FrameStateBuilder(RiResolvedMethod method, int maxLocals, int maxStackSize, StructuredGraph graph) { + assert graph != null; + this.method = method; + this.graph = graph; + this.locals = new ValueNode[maxLocals]; + // we always need at least one stack slot (for exceptions) + int stackSize = Math.max(1, maxStackSize); + this.stack = new ValueNode[stackSize]; + + int javaIndex = 0; + int index = 0; + if (!isStatic(method.accessFlags())) { + // add the receiver + LocalNode local = graph.unique(new LocalNode(javaIndex, StampFactory.declaredNonNull(method.holder()))); + storeLocal(javaIndex, local); + javaIndex = 1; + index = 1; + } + RiSignature sig = method.signature(); + int max = sig.argumentCount(false); + RiType accessingClass = method.holder(); + for (int i = 0; i < max; i++) { + RiType type = sig.argumentTypeAt(i, accessingClass); + CiKind kind = type.kind(false).stackKind(); + Stamp stamp; + if (kind == CiKind.Object && type instanceof RiResolvedType) { + RiResolvedType resolvedType = (RiResolvedType) type; + stamp = StampFactory.declared(resolvedType); + } else { + stamp = StampFactory.forKind(kind); + } + LocalNode local = graph.unique(new LocalNode(index, stamp)); + storeLocal(javaIndex, local); + javaIndex += stackSlots(kind); + index++; + } + this.locks = new ArrayList<>(); + } + + @Override + public String toString() { + return String.format("FrameStateBuilder[stackSize=%d]", stackIndex); + } + + public void initializeFrom(FrameState other) { + assert locals.length == other.localsSize() : "expected: " + locals.length + ", actual: " + other.localsSize(); + assert stack.length >= other.stackSize() : "expected: <=" + stack.length + ", actual: " + other.stackSize(); + + this.stackIndex = other.stackSize(); + for (int i = 0; i < other.localsSize(); i++) { + locals[i] = other.localAt(i); + } + for (int i = 0; i < other.stackSize(); i++) { + stack[i] = other.stackAt(i); + } + locks.clear(); + for (int i = 0; i < other.locksSize(); i++) { + locks.add(other.lockAt(i)); + } + this.rethrowException = other.rethrowException(); + } + + public FrameState create(int bci) { + return graph.add(new FrameState(method, bci, locals, stack, stackIndex, locks, rethrowException)); + } + + public FrameState duplicateWithException(int bci, ValueNode exceptionObject) { + FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, locks, true)); + frameState.setOuterFrameState(outerFrameState()); + return frameState; + } + + /** + * Pushes an instruction onto the stack with the expected type. + * @param kind the type expected for this instruction + * @param x the instruction to push onto the stack + */ + public void push(CiKind kind, ValueNode x) { + assert kind != CiKind.Void; + xpush(assertKind(kind, x)); + if (isTwoSlot(kind)) { + xpush(null); + } + } + + /** + * Pushes a value onto the stack without checking the type. + * @param x the instruction to push onto the stack + */ + public void xpush(ValueNode x) { + assert x == null || !x.isDeleted(); + assert x == null || (x.kind() != CiKind.Void && x.kind() != CiKind.Illegal) : "unexpected value: " + x; + stack[stackIndex++] = x; + } + + /** + * Pushes a value onto the stack and checks that it is an int. + * @param x the instruction to push onto the stack + */ + public void ipush(ValueNode x) { + xpush(assertInt(x)); + } + + /** + * Pushes a value onto the stack and checks that it is a float. + * @param x the instruction to push onto the stack + */ + public void fpush(ValueNode x) { + xpush(assertFloat(x)); + } + + /** + * Pushes a value onto the stack and checks that it is an object. + * @param x the instruction to push onto the stack + */ + public void apush(ValueNode x) { + xpush(assertObject(x)); + } + + /** + * Pushes a value onto the stack and checks that it is a JSR return address. + * @param x the instruction to push onto the stack + */ + public void jpush(ValueNode x) { + xpush(assertJsr(x)); + } + + /** + * Pushes a value onto the stack and checks that it is a long. + * + * @param x the instruction to push onto the stack + */ + public void lpush(ValueNode x) { + xpush(assertLong(x)); + xpush(null); + } + + /** + * Pushes a value onto the stack and checks that it is a double. + * @param x the instruction to push onto the stack + */ + public void dpush(ValueNode x) { + xpush(assertDouble(x)); + xpush(null); + } + + public void pushReturn(CiKind kind, ValueNode x) { + if (kind != CiKind.Void) { + push(kind.stackKind(), x); + } + } + + /** + * Pops an instruction off the stack with the expected type. + * @param kind the expected type + * @return the instruction on the top of the stack + */ + public ValueNode pop(CiKind kind) { + assert kind != CiKind.Void; + if (isTwoSlot(kind)) { + xpop(); + } + return assertKind(kind, xpop()); + } + + /** + * Pops a value off of the stack without checking the type. + * @return x the instruction popped off the stack + */ + public ValueNode xpop() { + ValueNode result = stack[--stackIndex]; + assert result == null || !result.isDeleted(); + return result; + } + + /** + * Pops a value off of the stack and checks that it is an int. + * @return x the instruction popped off the stack + */ + public ValueNode ipop() { + return assertInt(xpop()); + } + + /** + * Pops a value off of the stack and checks that it is a float. + * @return x the instruction popped off the stack + */ + public ValueNode fpop() { + return assertFloat(xpop()); + } + + /** + * Pops a value off of the stack and checks that it is an object. + * @return x the instruction popped off the stack + */ + public ValueNode apop() { + return assertObject(xpop()); + } + + /** + * Pops a value off of the stack and checks that it is a JSR return address. + * @return x the instruction popped off the stack + */ + public ValueNode jpop() { + return assertJsr(xpop()); + } + + /** + * Pops a value off of the stack and checks that it is a long. + * @return x the instruction popped off the stack + */ + public ValueNode lpop() { + assertHigh(xpop()); + return assertLong(xpop()); + } + + /** + * Pops a value off of the stack and checks that it is a double. + * @return x the instruction popped off the stack + */ + public ValueNode dpop() { + assertHigh(xpop()); + return assertDouble(xpop()); + } + + /** + * Pop the specified number of slots off of this stack and return them as an array of instructions. + * @param size the number of arguments off of the stack + * @return an array containing the arguments off of the stack + */ + public ValueNode[] popArguments(int slotSize, int argSize) { + int base = stackIndex - slotSize; + ValueNode[] r = new ValueNode[argSize]; + int argIndex = 0; + int stackindex = 0; + while (stackindex < slotSize) { + ValueNode element = stack[base + stackindex]; + assert element != null; + r[argIndex++] = element; + stackindex += stackSlots(element.kind()); + } + stackIndex = base; + return r; + } + + /** + * Peeks an element from the operand stack. + * @param argumentNumber The number of the argument, relative from the top of the stack (0 = top). + * Long and double arguments only count as one argument, i.e., null-slots are ignored. + * @return The peeked argument. + */ + public ValueNode peek(int argumentNumber) { + int idx = stackSize() - 1; + for (int i = 0; i < argumentNumber; i++) { + if (stackAt(idx) == null) { + idx--; + assert isTwoSlot(stackAt(idx).kind()); + } + idx--; + } + return stackAt(idx); + } + + /** + * Truncates this stack to the specified size. + * @param size the size to truncate to + */ + public void truncateStack(int size) { + stackIndex = size; + assert stackIndex >= 0; + } + + /** + * Clears all values on this stack. + */ + public void clearStack() { + stackIndex = 0; + } + + /** + * Loads the local variable at the specified index. + * + * @param i the index of the local variable to load + * @return the instruction that produced the specified local + */ + public ValueNode loadLocal(int i) { + ValueNode x = locals[i]; + if (x != null) { + if (x instanceof PhiNode) { + assert ((PhiNode) x).type() == PhiType.Value; + if (x.isDeleted()) { + return null; + } + } + assert !isTwoSlot(x.kind()) || locals[i + 1] == null || locals[i + 1] instanceof PhiNode; + } + return x; + } + + /** + * Stores a given local variable at the specified index. If the value is a {@linkplain CiKind#isDoubleWord() double word}, + * then the next local variable index is also overwritten. + * + * @param i the index at which to store + * @param x the instruction which produces the value for the local + */ + public void storeLocal(int i, ValueNode x) { + assert x == null || (x.kind() != CiKind.Void && x.kind() != CiKind.Illegal) : "unexpected value: " + x; + locals[i] = x; + if (isTwoSlot(x.kind())) { + // (tw) if this was a double word then kill i+1 + locals[i + 1] = null; + } + if (i > 0) { + // if there was a double word at i - 1, then kill it + ValueNode p = locals[i - 1]; + if (p != null && isTwoSlot(p.kind())) { + locals[i - 1] = null; + } + } + } + + /** + * Locks a new object within the specified IRScope. + * @param scope the IRScope in which this locking operation occurs + * @param obj the object being locked + */ + public void lock(MonitorObject obj) { + assert obj == null || (obj.kind() != CiKind.Void && obj.kind() != CiKind.Illegal) : "unexpected value: " + obj; + locks.add(obj); + } + + /** + * Unlock the lock on the top of the stack. + */ + public void unlock(MonitorObject obj) { + assert locks.get(locks.size() - 1) == obj; + locks.remove(locks.size() - 1); + } + + /** + * Get the value on the stack at the specified stack index. + * + * @param i the index into the stack, with {@code 0} being the bottom of the stack + * @return the instruction at the specified position in the stack + */ + public final ValueNode stackAt(int i) { + return stack[i]; + } + + /** + * Gets the value in the local variables at the specified index. + * + * @param i the index into the locals + * @return the instruction that produced the value for the specified local + */ + public final ValueNode localAt(int i) { + return locals[i]; + } + + /** + * Retrieves the lock at the specified index in the lock stack. + * @param i the index into the lock stack + * @return the instruction which produced the object at the specified location in the lock stack + */ + public final MonitorObject lockAt(int i) { + return locks.get(i); + } + + /** + * Returns the size of the local variables. + * + * @return the size of the local variables + */ + public int localsSize() { + return locals.length; + } + + /** + * Gets number of locks held by this frame state. + */ + public int locksSize() { + return locks.size(); + } + + /** + * Gets the current size (height) of the stack. + */ + public int stackSize() { + return stackIndex; + } + + public Iterator locals() { + return new ValueArrayIterator(locals); + } + + public Iterator stack() { + return new ValueArrayIterator(locals); + } + + public List locks() { + return Collections.unmodifiableList(locks); + } + + + private static class ValueArrayIterator implements Iterator { + private final ValueNode[] array; + private int index; + + public ValueArrayIterator(ValueNode[] array, int length) { + assert length <= array.length; + this.array = array; + this.index = 0; + } + + public ValueArrayIterator(ValueNode[] array) { + this(array, array.length); + } + + @Override + public boolean hasNext() { + return index < array.length; + } + + @Override + public ValueNode next() { + return array[index++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("cannot remove from array"); + } + + } + + + @Override + public FrameState duplicate(int bci) { + return create(bci); + } + + @Override + public ValueNode valueAt(int i) { + if (i < locals.length) { + return locals[i]; + } else if (i < locals.length + stackIndex) { + return stack[i - locals.length]; + } else { + return locks.get(i - locals.length - stack.length); + } + } + + @Override + public FrameState outerFrameState() { + return null; + } + + public FrameState duplicateWithoutStack(int bci) { + FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, locks, false)); + frameState.setOuterFrameState(outerFrameState()); + return frameState; + } + + @Override + public boolean rethrowException() { + return rethrowException; + } + + public void setRethrowException(boolean b) { + rethrowException = b; + } + + public static int stackSlots(CiKind kind) { + return isTwoSlot(kind) ? 2 : 1; + } + + public static boolean isTwoSlot(CiKind kind) { + assert kind != CiKind.Void && kind != CiKind.Illegal; + return kind == CiKind.Long || kind == CiKind.Double; + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderConfiguration.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import com.oracle.max.graal.compiler.*; +import com.oracle.max.graal.compiler.phases.*; + +public class GraphBuilderConfiguration { + private final boolean useBranchPrediction; + private final boolean eagerResolving; + private final PhasePlan plan; + + public GraphBuilderConfiguration(boolean useBranchPrediction, boolean eagerResolving, PhasePlan plan) { + this.useBranchPrediction = useBranchPrediction; + this.eagerResolving = eagerResolving; + this.plan = plan; + } + + public boolean useBranchPrediction() { + return useBranchPrediction; + } + + public boolean eagerResolving() { + return eagerResolving; + } + + public PhasePlan plan() { + return plan; + } + + public static GraphBuilderConfiguration getDefault() { + return getDefault(null); + } + + public static GraphBuilderConfiguration getDefault(PhasePlan plan) { + return new GraphBuilderConfiguration(GraalOptions.UseBranchPrediction, false, plan); + } + + public static GraphBuilderConfiguration getDeoptFreeDefault() { + return getDeoptFreeDefault(null); + } + + public static GraphBuilderConfiguration getDeoptFreeDefault(PhasePlan plan) { + return new GraphBuilderConfiguration(false, true, plan); + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,1748 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import static com.oracle.max.graal.java.Bytecodes.*; +import static java.lang.reflect.Modifier.*; + +import java.lang.reflect.*; +import java.util.*; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.cri.ri.*; +import com.oracle.max.cri.ri.RiType.*; +import com.oracle.max.criutils.*; +import com.oracle.max.graal.compiler.*; +import com.oracle.max.graal.compiler.phases.*; +import com.oracle.max.graal.compiler.schedule.*; +import com.oracle.max.graal.compiler.util.*; +import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.java.BlockMap.*; +import com.oracle.max.graal.nodes.*; +import com.oracle.max.graal.nodes.DeoptimizeNode.DeoptAction; +import com.oracle.max.graal.nodes.PhiNode.PhiType; +import com.oracle.max.graal.java.BlockMap.Block; +import com.oracle.max.graal.nodes.calc.*; +import com.oracle.max.graal.nodes.extended.*; +import com.oracle.max.graal.nodes.java.*; +import com.oracle.max.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.max.graal.nodes.spi.*; + +/** + * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph. + */ +public final class GraphBuilderPhase extends Phase { + + /** + * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace + * the bytecode instructions as they are parsed. + */ + public static final int TRACELEVEL_INSTRUCTIONS = 1; + + /** + * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace + * the frame state before each bytecode instruction as it is parsed. + */ + public static final int TRACELEVEL_STATE = 2; + + private StructuredGraph currentGraph; + + private final CiStatistics stats; + private final RiRuntime runtime; + private RiConstantPool constantPool; + private RiExceptionHandler[] exceptionHandlers; + private RiResolvedMethod method; + + private BytecodeStream stream; // the bytecode stream + private final LogStream log; + + private FrameStateBuilder frameState; // the current execution state + private Block currentBlock; + + private int nextBlockNumber; + + private ValueNode methodSynchronizedObject; + private ExceptionBlock unwindBlock; + private Block returnBlock; + + // the worklist of blocks, sorted by depth first number + private final PriorityQueue workList = new PriorityQueue<>(10, new Comparator() { + public int compare(Block o1, Block o2) { + return o1.blockID - o2.blockID; + } + }); + + private FixedWithNextNode lastInstr; // the last instruction added + + private Set blocksOnWorklist; + private Set blocksVisited; + + private BitSet canTrapBitSet; + + public static final Map cachedGraphs = new WeakHashMap<>(); + + private final GraphBuilderConfiguration config; + + public GraphBuilderPhase(RiRuntime runtime) { + this(runtime, null); + } + + public GraphBuilderPhase(RiRuntime runtime, CiStatistics stats) { + this(runtime, stats, GraphBuilderConfiguration.getDefault()); + } + + public GraphBuilderPhase(RiRuntime runtime, CiStatistics stats, GraphBuilderConfiguration config) { + this.config = config; + this.runtime = runtime; + this.stats = stats; + this.log = GraalOptions.TraceBytecodeParserLevel > 0 ? new LogStream(TTY.out()) : null; + } + + @Override + protected void run(StructuredGraph graph) { + method = graph.method(); + assert method.code() != null : "method must contain bytecodes: " + method; + this.stream = new BytecodeStream(method.code()); + this.constantPool = method.getConstantPool(); + this.blocksOnWorklist = new HashSet<>(); + this.blocksVisited = new HashSet<>(); + unwindBlock = null; + returnBlock = null; + methodSynchronizedObject = null; + exceptionHandlers = null; + assert graph != null; + this.currentGraph = graph; + this.frameState = new FrameStateBuilder(method, method.maxLocals(), method.maxStackSize(), graph); + build(); + } + + @Override + protected String getDetailedName() { + return getName() + " " + CiUtil.format("%H.%n(%p):%r", method); + } + + private BlockMap createBlockMap() { + BlockMap map = new BlockMap(method, config.useBranchPrediction()); + map.build(); + if (stats != null) { + stats.bytecodeCount += method.code().length; + } + + if (currentContext.isObserved()) { + String label = CiUtil.format("BlockListBuilder %f %R %H.%n(%P)", method); + currentContext.observable.fireCompilationEvent(label, map); + } + return map; + } + + private void build() { + if (log != null) { + log.println(); + log.println("Compiling " + method); + } + + // compute the block map, setup exception handlers and get the entrypoint(s) + BlockMap blockMap = createBlockMap(); + this.canTrapBitSet = blockMap.canTrap; + + exceptionHandlers = blockMap.exceptionHandlers(); + if (stats != null) { + stats.blockCount += blockMap.blocks.size(); + } + nextBlockNumber = blockMap.blocks.size(); + + lastInstr = currentGraph.start(); + if (isSynchronized(method.accessFlags())) { + // add a monitor enter to the start block + currentGraph.start().setStateAfter(frameState.create(FrameState.BEFORE_BCI)); + methodSynchronizedObject = synchronizedObject(frameState, method); + lastInstr = genMonitorEnter(methodSynchronizedObject); + } + + // finish the start block + ((AbstractStateSplit) lastInstr).setStateAfter(frameState.create(0)); + if (blockMap.startBlock.isLoopHeader) { + appendGoto(createTarget(blockMap.startBlock, frameState)); + } else { + blockMap.startBlock.firstInstruction = lastInstr; + } + addToWorkList(blockMap.startBlock); + + iterateAllBlocks(); + connectLoopEndToBegin(); + + // remove Placeholders (except for loop exits) + for (PlaceholderNode n : currentGraph.getNodes(PlaceholderNode.class)) { + n.replaceAndDelete(n.next()); + } + + // remove dead FrameStates + for (Node n : currentGraph.getNodes(FrameState.class)) { + if (n.usages().size() == 0 && n.predecessor() == null) { + n.safeDelete(); + } + } + + if (GraalOptions.CacheGraphs && !currentGraph.hasNode(DeoptimizeNode.class)) { + cachedGraphs.put(method, currentGraph.copy()); + } + } + + private int nextBlockNumber() { + if (stats != null) { + stats.blockCount++; + } + return nextBlockNumber++; + } + + private Block unwindBlock(int bci) { + if (unwindBlock == null) { + unwindBlock = new ExceptionBlock(); + unwindBlock.startBci = -1; + unwindBlock.endBci = -1; + unwindBlock.deoptBci = bci; + unwindBlock.blockID = nextBlockNumber(); + addToWorkList(unwindBlock); + } + return unwindBlock; + } + + private Block returnBlock(int bci) { + if (returnBlock == null) { + returnBlock = new Block(); + returnBlock.startBci = bci; + returnBlock.endBci = bci; + returnBlock.blockID = nextBlockNumber(); + addToWorkList(returnBlock); + } + return returnBlock; + } + + private void markOnWorkList(Block block) { + blocksOnWorklist.add(block); + } + + private boolean isOnWorkList(Block block) { + return blocksOnWorklist.contains(block); + } + + private void markVisited(Block block) { + blocksVisited.add(block); + } + + private boolean isVisited(Block block) { + return blocksVisited.contains(block); + } + + public void mergeOrClone(Block target, FrameStateAccess newState) { + AbstractStateSplit first = (AbstractStateSplit) target.firstInstruction; + + if (target.isLoopHeader && isVisited(target)) { + first = (AbstractStateSplit) loopBegin(target).loopEnd().predecessor(); + } + + int bci = target.startBci; + if (target instanceof ExceptionBlock) { + bci = ((ExceptionBlock) target).deoptBci; + } + + FrameState existingState = first.stateAfter(); + + if (existingState == null) { + // copy state because it is modified + first.setStateAfter(newState.duplicate(bci)); + } else { + if (!GraalOptions.AssumeVerifiedBytecode && !existingState.isCompatibleWith(newState)) { + // stacks or locks do not match--bytecodes would not verify + TTY.println(existingState.toString()); + TTY.println(newState.duplicate(0).toString()); + throw new CiBailout("stack or locks do not match"); + } + assert existingState.localsSize() == newState.localsSize(); + assert existingState.stackSize() == newState.stackSize(); + + if (first instanceof PlaceholderNode) { + PlaceholderNode p = (PlaceholderNode) first; + if (p.predecessor() == null) { + p.setStateAfter(newState.duplicate(bci)); + return; + } else { + MergeNode merge = currentGraph.add(new MergeNode()); + FixedNode next = p.next(); + EndNode end = currentGraph.add(new EndNode()); + p.setNext(end); + merge.setNext(next); + merge.addEnd(end); + merge.setStateAfter(existingState); + p.setStateAfter(existingState.duplicate(bci)); + if (!(next instanceof LoopEndNode)) { + target.firstInstruction = merge; + } + first = merge; + } + } + + existingState.merge((MergeNode) first, newState); + } + } + + public BytecodeStream stream() { + return stream; + } + + public int bci() { + return stream.currentBCI(); + } + + private void loadLocal(int index, CiKind kind) { + frameState.push(kind, frameState.loadLocal(index)); + } + + private void storeLocal(CiKind kind, int index) { + frameState.storeLocal(index, frameState.pop(kind)); + } + + public static boolean covers(RiExceptionHandler handler, int bci) { + return handler.startBCI() <= bci && bci < handler.endBCI(); + } + + public static boolean isCatchAll(RiExceptionHandler handler) { + return handler.catchTypeCPI() == 0; + } + + private BeginNode handleException(ValueNode exceptionObject, int bci) { + assert bci == FrameState.BEFORE_BCI || bci == bci() : "invalid bci"; + + if (GraalOptions.UseExceptionProbability && method.invocationCount() > GraalOptions.MatureInvocationCount) { + if (bci != FrameState.BEFORE_BCI && exceptionObject == null && method.exceptionProbability(bci) == 0) { + return null; + } + } + + 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 = 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; + } + } + } + + BeginNode p = currentGraph.add(new BeginNode()); + p.setStateAfter(frameState.duplicateWithoutStack(bci)); + + ValueNode currentExceptionObject; + ExceptionObjectNode newObj = null; + if (exceptionObject == null) { + newObj = currentGraph.add(new ExceptionObjectNode()); + currentExceptionObject = newObj; + } else { + currentExceptionObject = exceptionObject; + } + FrameState stateWithException = frameState.duplicateWithException(bci, currentExceptionObject); + if (newObj != null) { + newObj.setStateAfter(stateWithException); + } + FixedNode target = createTarget(dispatchBlock, stateWithException); + if (exceptionObject == null) { + ExceptionObjectNode eObj = (ExceptionObjectNode) currentExceptionObject; + eObj.setNext(target); + p.setNext(eObj); + } else { + p.setNext(target); + } + return p; + } + + private void genLoadConstant(int cpi) { + Object con = constantPool.lookupConstant(cpi); + + if (con instanceof RiType) { + // this is a load of class constant which might be unresolved + RiType riType = (RiType) con; + if (riType instanceof RiResolvedType) { + frameState.push(CiKind.Object, append(ConstantNode.forCiConstant(((RiResolvedType) riType).getEncoding(Representation.JavaClass), runtime, currentGraph))); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.push(CiKind.Object, append(ConstantNode.forObject(null, runtime, currentGraph))); + } + } else if (con instanceof CiConstant) { + CiConstant constant = (CiConstant) con; + frameState.push(constant.kind.stackKind(), appendConstant(constant)); + } else { + throw new Error("lookupConstant returned an object of incorrect type"); + } + } + + private void genLoadIndexed(CiKind kind) { + emitExplicitExceptions(frameState.peek(1), frameState.peek(0)); + + ValueNode index = frameState.ipop(); + ValueNode array = frameState.apop(); + ValueNode length = append(currentGraph.add(new ArrayLengthNode(array))); + ValueNode v = append(currentGraph.add(new LoadIndexedNode(array, index, length, kind))); + frameState.push(kind.stackKind(), v); + } + + private void genStoreIndexed(CiKind kind) { + emitExplicitExceptions(frameState.peek(2), frameState.peek(1)); + + ValueNode value = frameState.pop(kind.stackKind()); + ValueNode index = frameState.ipop(); + ValueNode array = frameState.apop(); + ValueNode length = append(currentGraph.add(new ArrayLengthNode(array))); + StoreIndexedNode result = currentGraph.add(new StoreIndexedNode(array, index, length, kind, value)); + append(result); + } + + private void stackOp(int opcode) { + switch (opcode) { + case POP: { + frameState.xpop(); + break; + } + case POP2: { + frameState.xpop(); + frameState.xpop(); + break; + } + case DUP: { + ValueNode w = frameState.xpop(); + frameState.xpush(w); + frameState.xpush(w); + break; + } + case DUP_X1: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + frameState.xpush(w1); + frameState.xpush(w2); + frameState.xpush(w1); + break; + } + case DUP_X2: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + ValueNode w3 = frameState.xpop(); + frameState.xpush(w1); + frameState.xpush(w3); + frameState.xpush(w2); + frameState.xpush(w1); + break; + } + case DUP2: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + frameState.xpush(w2); + frameState.xpush(w1); + frameState.xpush(w2); + frameState.xpush(w1); + break; + } + case DUP2_X1: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + ValueNode w3 = frameState.xpop(); + frameState.xpush(w2); + frameState.xpush(w1); + frameState.xpush(w3); + frameState.xpush(w2); + frameState.xpush(w1); + break; + } + case DUP2_X2: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + ValueNode w3 = frameState.xpop(); + ValueNode w4 = frameState.xpop(); + frameState.xpush(w2); + frameState.xpush(w1); + frameState.xpush(w4); + frameState.xpush(w3); + frameState.xpush(w2); + frameState.xpush(w1); + break; + } + case SWAP: { + ValueNode w1 = frameState.xpop(); + ValueNode w2 = frameState.xpop(); + frameState.xpush(w1); + frameState.xpush(w2); + break; + } + default: + throw Util.shouldNotReachHere(); + } + + } + + private void genArithmeticOp(CiKind result, int opcode, boolean canTrap) { + ValueNode y = frameState.pop(result); + ValueNode x = frameState.pop(result); + boolean isStrictFP = isStrict(method.accessFlags()); + ArithmeticNode v; + switch(opcode){ + case IADD: + case LADD: v = new IntegerAddNode(result, x, y); break; + case FADD: + case DADD: v = new FloatAddNode(result, x, y, isStrictFP); break; + case ISUB: + case LSUB: v = new IntegerSubNode(result, x, y); break; + case FSUB: + case DSUB: v = new FloatSubNode(result, x, y, isStrictFP); break; + case IMUL: + case LMUL: v = new IntegerMulNode(result, x, y); break; + case FMUL: + case DMUL: v = new FloatMulNode(result, x, y, isStrictFP); break; + case IDIV: + case LDIV: v = new IntegerDivNode(result, x, y); break; + case FDIV: + case DDIV: v = new FloatDivNode(result, x, y, isStrictFP); break; + case IREM: + case LREM: v = new IntegerRemNode(result, x, y); break; + case FREM: + case DREM: v = new FloatRemNode(result, x, y, isStrictFP); break; + default: + throw new CiBailout("should not reach"); + } + ValueNode result1 = append(currentGraph.unique(v)); + if (canTrap) { + append(currentGraph.add(new ValueAnchorNode(result1))); + } + frameState.push(result, result1); + } + + private void genNegateOp(CiKind kind) { + frameState.push(kind, append(currentGraph.unique(new NegateNode(frameState.pop(kind))))); + } + + private void genShiftOp(CiKind kind, int opcode) { + ValueNode s = frameState.ipop(); + ValueNode x = frameState.pop(kind); + ShiftNode v; + switch(opcode){ + case ISHL: + case LSHL: v = new LeftShiftNode(kind, x, s); break; + case ISHR: + case LSHR: v = new RightShiftNode(kind, x, s); break; + case IUSHR: + case LUSHR: v = new UnsignedRightShiftNode(kind, x, s); break; + default: + throw new CiBailout("should not reach"); + } + frameState.push(kind, append(currentGraph.unique(v))); + } + + private void genLogicOp(CiKind kind, int opcode) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + LogicNode v; + switch(opcode){ + case IAND: + case LAND: v = new AndNode(kind, x, y); break; + case IOR: + case LOR: v = new OrNode(kind, x, y); break; + case IXOR: + case LXOR: v = new XorNode(kind, x, y); break; + default: + throw new CiBailout("should not reach"); + } + frameState.push(kind, append(currentGraph.unique(v))); + } + + private void genCompareOp(CiKind kind, boolean isUnorderedLess) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + frameState.ipush(append(currentGraph.unique(new NormalizeCompareNode(x, y, isUnorderedLess)))); + } + + private void genConvert(ConvertNode.Op opcode) { + ValueNode input = frameState.pop(opcode.from.stackKind()); + frameState.push(opcode.to.stackKind(), append(currentGraph.unique(new ConvertNode(opcode, input)))); + } + + private void genIncrement() { + int index = stream().readLocalIndex(); + int delta = stream().readIncrement(); + ValueNode x = frameState.localAt(index); + ValueNode y = append(ConstantNode.forInt(delta, currentGraph)); + frameState.storeLocal(index, append(currentGraph.unique(new IntegerAddNode(CiKind.Int, x, y)))); + } + + private void genGoto() { + appendGoto(createTarget(currentBlock.successors.get(0), frameState)); + assert currentBlock.normalSuccessors == 1; + } + + private void ifNode(ValueNode x, Condition cond, ValueNode y) { + assert !x.isDeleted() && !y.isDeleted(); + double probability = method.branchProbability(bci()); + if (probability < 0) { + if (GraalOptions.TraceProbability) { + TTY.println("missing probability in " + method + " at bci " + bci()); + } + probability = 0.5; + } + + IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(x, cond, y)), probability)); + append(ifNode); + ifNode.setTrueSuccessor(BeginNode.begin(createTarget(currentBlock.successors.get(0), frameState))); + ifNode.setFalseSuccessor(BeginNode.begin(createTarget(currentBlock.successors.get(1), frameState))); + + assert currentBlock.normalSuccessors == 2 : currentBlock.normalSuccessors; + } + + private void genIfZero(Condition cond) { + ValueNode y = appendConstant(CiConstant.INT_0); + ValueNode x = frameState.ipop(); + ifNode(x, cond, y); + } + + private void genIfNull(Condition cond) { + ValueNode y = appendConstant(CiConstant.NULL_OBJECT); + ValueNode x = frameState.apop(); + ifNode(x, cond, y); + } + + private void genIfSame(CiKind kind, Condition cond) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + assert !x.isDeleted() && !y.isDeleted(); + ifNode(x, cond, y); + } + + private void genThrow(int bci) { + ValueNode exception = frameState.apop(); + FixedGuardNode node = currentGraph.add(new FixedGuardNode(currentGraph.unique(new NullCheckNode(exception, false)))); + append(node); + append(handleException(exception, bci)); + } + + private RiType lookupType(int cpi, int bytecode) { + eagerResolving(cpi, bytecode); + RiType result = constantPool.lookupType(cpi, bytecode); + assert !config.eagerResolving() || result instanceof RiResolvedType; + return result; + } + + private RiMethod lookupMethod(int cpi, int opcode) { + eagerResolving(cpi, opcode); + RiMethod result = constantPool.lookupMethod(cpi, opcode); + assert !config.eagerResolving() || ((result instanceof RiResolvedMethod) && ((RiResolvedMethod) result).holder().isInitialized()); + return result; + } + + private RiField lookupField(int cpi, int opcode) { + eagerResolving(cpi, opcode); + RiField result = constantPool.lookupField(cpi, opcode); + assert !config.eagerResolving() || (result instanceof RiResolvedField && ((RiResolvedField) result).holder().isInitialized()); + return result; + } + + private void eagerResolving(int cpi, int bytecode) { + if (config.eagerResolving()) { + constantPool.loadReferencedType(cpi, bytecode); + } + } + + private void genCheckCast() { + int cpi = stream().readCPI(); + RiType type = lookupType(cpi, CHECKCAST); + boolean initialized = type instanceof RiResolvedType; + if (initialized) { + ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, true); + ValueNode object = frameState.apop(); + AnchorNode anchor = currentGraph.add(new AnchorNode()); + append(anchor); + CheckCastNode checkCast = currentGraph.unique(new CheckCastNode(anchor, typeInstruction, (RiResolvedType) type, object)); + append(currentGraph.add(new ValueAnchorNode(checkCast))); + frameState.apush(checkCast); + } else { + ValueNode object = frameState.apop(); + append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new CompareNode(object, Condition.EQ, ConstantNode.forObject(null, runtime, currentGraph)))))); + frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); + } + } + + private void genInstanceOf() { + int cpi = stream().readCPI(); + RiType type = lookupType(cpi, INSTANCEOF); + ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, type, type instanceof RiResolvedType); + ValueNode object = frameState.apop(); + if (typeInstruction != null) { + frameState.ipush(append(MaterializeNode.create(currentGraph.unique(new InstanceOfNode(typeInstruction, (RiResolvedType) type, object, false)), currentGraph))); + } else { + frameState.ipush(appendConstant(CiConstant.INT_0)); + } + } + + void genNewInstance(int cpi) { + RiType type = lookupType(cpi, NEW); + if (type instanceof RiResolvedType) { + NewInstanceNode n = currentGraph.add(new NewInstanceNode((RiResolvedType) type)); + frameState.apush(append(n)); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); + } + } + + /** + * Gets the kind of array elements for the array type code that appears + * in a {@link Bytecodes#NEWARRAY} bytecode. + * @param code the array type code + * @return the kind from the array type code + */ + public static CiKind arrayTypeCodeToKind(int code) { + // Checkstyle: stop + switch (code) { + case 4: return CiKind.Boolean; + case 5: return CiKind.Char; + case 6: return CiKind.Float; + case 7: return CiKind.Double; + case 8: return CiKind.Byte; + case 9: return CiKind.Short; + case 10: return CiKind.Int; + case 11: return CiKind.Long; + default: throw new IllegalArgumentException("unknown array type code: " + code); + } + // Checkstyle: resume + } + + private void genNewTypeArray(int typeCode) { + CiKind kind = arrayTypeCodeToKind(typeCode); + RiResolvedType elementType = runtime.asRiType(kind); + NewTypeArrayNode nta = currentGraph.add(new NewTypeArrayNode(frameState.ipop(), elementType)); + frameState.apush(append(nta)); + } + + private void genNewObjectArray(int cpi) { + RiType type = lookupType(cpi, ANEWARRAY); + ValueNode length = frameState.ipop(); + if (type instanceof RiResolvedType) { + NewArrayNode n = currentGraph.add(new NewObjectArrayNode((RiResolvedType) type, length)); + frameState.apush(append(n)); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); + } + + } + + private void genNewMultiArray(int cpi) { + RiType type = lookupType(cpi, MULTIANEWARRAY); + int rank = stream().readUByte(bci() + 3); + ValueNode[] dims = new ValueNode[rank]; + for (int i = rank - 1; i >= 0; i--) { + dims[i] = frameState.ipop(); + } + if (type instanceof RiResolvedType) { + FixedWithNextNode n = currentGraph.add(new NewMultiArrayNode((RiResolvedType) type, dims)); + frameState.apush(append(n)); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); + } + } + + private void genGetField(RiField field) { + emitExplicitExceptions(frameState.peek(0), null); + + CiKind kind = field.kind(false); + ValueNode receiver = frameState.apop(); + if ((field instanceof RiResolvedField) && ((RiResolvedField) field).holder().isInitialized()) { + LoadFieldNode load = currentGraph.add(new LoadFieldNode(receiver, (RiResolvedField) field)); + appendOptimizedLoadField(kind, load); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); + } + } + + public static class ExceptionInfo { + + public final FixedWithNextNode exceptionEdge; + public final ValueNode exception; + + public ExceptionInfo(FixedWithNextNode exceptionEdge, ValueNode exception) { + this.exceptionEdge = exceptionEdge; + this.exception = exception; + } + } + + private ExceptionInfo emitNullCheck(ValueNode receiver) { + PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); + PlaceholderNode falseSucc = currentGraph.add(new PlaceholderNode()); + IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new NullCheckNode(receiver, false)), trueSucc, falseSucc, 1)); + + append(ifNode); + lastInstr = trueSucc; + + if (GraalOptions.OmitHotExceptionStacktrace) { + ValueNode exception = ConstantNode.forObject(new NullPointerException(), runtime, currentGraph); + return new ExceptionInfo(falseSucc, exception); + } else { + RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateNullPointerException)); + call.setStateAfter(frameState.duplicate(bci())); + falseSucc.setNext(call); + return new ExceptionInfo(call, call); + } + } + + private ExceptionInfo emitBoundsCheck(ValueNode index, ValueNode length) { + PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); + PlaceholderNode falseSucc = currentGraph.add(new PlaceholderNode()); + IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(index, Condition.BT, length)), trueSucc, falseSucc, 1)); + + append(ifNode); + lastInstr = trueSucc; + + if (GraalOptions.OmitHotExceptionStacktrace) { + ValueNode exception = ConstantNode.forObject(new ArrayIndexOutOfBoundsException(), runtime, currentGraph); + return new ExceptionInfo(falseSucc, exception); + } else { + RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateOutOfBoundsException, new ValueNode[] {index})); + call.setStateAfter(frameState.duplicate(bci())); + falseSucc.setNext(call); + return new ExceptionInfo(call, call); + } + } + + private void emitExplicitExceptions(ValueNode receiver, ValueNode outOfBoundsIndex) { + assert receiver != null; + + 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.addEnd(end); + phi.addInput(info.exception); + } + merge.setStateAfter(frameState.duplicate(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.duplicateWithException(bci(), exception.exception))); + } + if (GraalOptions.Meter) { + currentContext.metrics.ExplicitExceptions++; + } + } + } + + private void genPutField(RiField field) { + emitExplicitExceptions(frameState.peek(1), null); + + ValueNode value = frameState.pop(field.kind(false).stackKind()); + ValueNode receiver = frameState.apop(); + if (field instanceof RiResolvedField && ((RiResolvedField) field).holder().isInitialized()) { + StoreFieldNode store = currentGraph.add(new StoreFieldNode(receiver, (RiResolvedField) field, value)); + appendOptimizedStoreField(store); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + } + } + + private void genGetStatic(RiField field) { + RiType holder = field.holder(); + boolean isInitialized = (field instanceof RiResolvedField) && ((RiResolvedType) holder).isInitialized(); + CiConstant constantValue = null; + if (isInitialized) { + constantValue = ((RiResolvedField) field).constantValue(null); + } + if (constantValue != null) { + frameState.push(constantValue.kind.stackKind(), appendConstant(constantValue)); + } else { + ValueNode container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, isInitialized); + CiKind kind = field.kind(false); + if (container != null) { + LoadFieldNode load = currentGraph.add(new LoadFieldNode(container, (RiResolvedField) field)); + appendOptimizedLoadField(kind, load); + } else { + // deopt will be generated by genTypeOrDeopt, not needed here + frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); + } + } + } + + private void genPutStatic(RiField field) { + RiType holder = field.holder(); + ValueNode container = genTypeOrDeopt(RiType.Representation.StaticFields, holder, field instanceof RiResolvedField && ((RiResolvedType) holder).isInitialized()); + ValueNode value = frameState.pop(field.kind(false).stackKind()); + if (container != null) { + StoreFieldNode store = currentGraph.add(new StoreFieldNode(container, (RiResolvedField) field, value)); + appendOptimizedStoreField(store); + } else { + // deopt will be generated by genTypeOrDeopt, not needed here + } + } + + private ConstantNode genTypeOrDeopt(RiType.Representation representation, RiType holder, boolean initialized) { + if (initialized) { + return appendConstant(((RiResolvedType) holder).getEncoding(representation)); + } else { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + return null; + } + } + + private void appendOptimizedStoreField(StoreFieldNode store) { + append(store); + } + + private void appendOptimizedLoadField(CiKind kind, LoadFieldNode load) { + // append the load to the instruction + ValueNode optimized = append(load); + frameState.push(kind.stackKind(), optimized); + } + + private void genInvokeStatic(RiMethod target) { + if (target instanceof RiResolvedMethod) { + RiResolvedMethod resolvedTarget = (RiResolvedMethod) target; + RiResolvedType holder = resolvedTarget.holder(); + if (!holder.isInitialized() && GraalOptions.ResolveClassBeforeStaticInvoke) { + genInvokeDeopt(target, false); + } else { + ValueNode[] args = frameState.popArguments(resolvedTarget.signature().argumentSlots(false), resolvedTarget.signature().argumentCount(false)); + appendInvoke(InvokeKind.Static, resolvedTarget, args); + } + } else { + genInvokeDeopt(target, false); + } + } + + private void genInvokeInterface(RiMethod target) { + if (target instanceof RiResolvedMethod) { + ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); + genInvokeIndirect(InvokeKind.Interface, (RiResolvedMethod) target, args); + } else { + genInvokeDeopt(target, true); + } + } + + private void genInvokeVirtual(RiMethod target) { + if (target instanceof RiResolvedMethod) { + ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); + genInvokeIndirect(InvokeKind.Virtual, (RiResolvedMethod) target, args); + } else { + genInvokeDeopt(target, true); + } + + } + + private void genInvokeSpecial(RiMethod target) { + if (target instanceof RiResolvedMethod) { + assert target != null; + assert target.signature() != null; + ValueNode[] args = frameState.popArguments(target.signature().argumentSlots(true), target.signature().argumentCount(true)); + invokeDirect((RiResolvedMethod) target, args); + } else { + genInvokeDeopt(target, true); + } + } + + private void genInvokeDeopt(RiMethod unresolvedTarget, boolean withReceiver) { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile))); + frameState.popArguments(unresolvedTarget.signature().argumentSlots(withReceiver), unresolvedTarget.signature().argumentCount(withReceiver)); + CiKind kind = unresolvedTarget.signature().returnKind(false); + if (kind != CiKind.Void) { + frameState.push(kind.stackKind(), append(ConstantNode.defaultForKind(kind, currentGraph))); + } + } + + private void genInvokeIndirect(InvokeKind invokeKind, RiResolvedMethod target, ValueNode[] args) { + ValueNode receiver = args[0]; + // attempt to devirtualize the call + RiResolvedType klass = target.holder(); + + // 0. check for trivial cases + if (target.canBeStaticallyBound() && !isAbstract(target.accessFlags())) { + // check for trivial cases (e.g. final methods, nonvirtual methods) + invokeDirect(target, args); + return; + } + // 1. check if the exact type of the receiver can be determined + RiResolvedType exact = getExactType(klass, receiver); + if (exact != null) { + // either the holder class is exact, or the receiver object has an exact type + invokeDirect(exact.resolveMethodImpl(target), args); + return; + } + // devirtualization failed, produce an actual invokevirtual + appendInvoke(invokeKind, target, args); + } + + private void invokeDirect(RiResolvedMethod target, ValueNode[] args) { + appendInvoke(InvokeKind.Special, target, args); + } + + private void appendInvoke(InvokeKind invokeKind, RiResolvedMethod targetMethod, ValueNode[] args) { + CiKind resultType = targetMethod.signature().returnKind(false); + if (GraalOptions.DeoptALot) { + DeoptimizeNode deoptimize = currentGraph.add(new DeoptimizeNode(DeoptAction.None)); + 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())); + 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()))); + frameState.pushReturn(resultType, result); + } + } + } + + private RiResolvedType getExactType(RiResolvedType staticType, ValueNode receiver) { + RiResolvedType exact = staticType.exactType(); + if (exact == null) { + exact = receiver.exactType(); + if (exact == null) { + if (receiver.isConstant()) { + exact = runtime.getTypeOf(receiver.asConstant()); + } + if (exact == null) { + RiType declared = receiver.declaredType(); + if (declared instanceof RiResolvedType) { + exact = ((RiResolvedType) declared).exactType(); + } + } + } + } + return exact; + } + + private void callRegisterFinalizer() { + // append a call to the finalizer registration + append(currentGraph.add(new RegisterFinalizerNode(frameState.loadLocal(0)))); + } + + private void genReturn(ValueNode x) { + frameState.clearStack(); + if (x != null) { + frameState.push(x.kind(), x); + } + appendGoto(createTarget(returnBlock(bci()), frameState)); + } + + private MonitorEnterNode genMonitorEnter(ValueNode x) { + MonitorObject monitorObject = currentGraph.add(new MonitorObject(x)); + MonitorEnterNode monitorEnter = currentGraph.add(new MonitorEnterNode(monitorObject)); + frameState.lock(monitorObject); + appendWithBCI(monitorEnter); + return monitorEnter; + } + + private MonitorExitNode genMonitorExit(ValueNode x) { + if (frameState.locksSize() <= 0) { + throw new CiBailout("monitor stack underflow"); + } + MonitorObject monitorObject = frameState.lockAt(frameState.locksSize() - 1); + + // We only compile methods with balanced monitors. However, x might be a phi function + // that can be optimized away later on, so we have to disable the check for phi functions. + assert x == monitorObject.owner() || x instanceof PhiNode; + + MonitorExitNode monitorExit = currentGraph.add(new MonitorExitNode(monitorObject)); + appendWithBCI(monitorExit); + frameState.unlock(monitorObject); + return monitorExit; + } + + private void genJsr(int dest) { + Block successor = currentBlock.jsrSuccessor; + assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci(); + JsrScope scope = currentBlock.jsrScope; + if (!successor.jsrScope.pop().equals(scope)) { + throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); + } + if (successor.jsrScope.nextReturnAddress() != stream().nextBCI()) { + throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); + } + frameState.push(CiKind.Jsr, ConstantNode.forJsr(stream().nextBCI(), currentGraph)); + appendGoto(createTarget(successor, frameState)); + } + + private void genRet(int localIndex) { + Block successor = currentBlock.retSuccessor; + ValueNode local = frameState.loadLocal(localIndex); + JsrScope scope = currentBlock.jsrScope; + int retAddress = scope.nextReturnAddress(); + append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new CompareNode(local, Condition.EQ, ConstantNode.forJsr(retAddress, currentGraph)))))); + if (!successor.jsrScope.equals(scope.pop())) { + throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)"); + } + appendGoto(createTarget(successor, frameState)); + } + + private void genTableswitch() { + int bci = bci(); + ValueNode value = frameState.ipop(); + BytecodeTableSwitch ts = new BytecodeTableSwitch(stream(), bci); + + int nofCases = ts.numberOfCases() + 1; // including default case + assert currentBlock.normalSuccessors == nofCases; + + TableSwitchNode tableSwitch = currentGraph.add(new TableSwitchNode(value, ts.lowKey(), switchProbability(nofCases, bci))); + for (int i = 0; i < nofCases; ++i) { + tableSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); + } + append(tableSwitch); + } + + private double[] switchProbability(int numberOfCases, int bci) { + double[] prob = method.switchProbability(bci); + if (prob != null) { + assert prob.length == numberOfCases; + } else { + if (GraalOptions.TraceProbability) { + TTY.println("Missing probability (switch) in " + method + " at bci " + bci); + } + prob = new double[numberOfCases]; + for (int i = 0; i < numberOfCases; i++) { + prob[i] = 1.0d / numberOfCases; + } + } + return prob; + } + + private void genLookupswitch() { + int bci = bci(); + ValueNode value = frameState.ipop(); + BytecodeLookupSwitch ls = new BytecodeLookupSwitch(stream(), bci); + + int nofCases = ls.numberOfCases() + 1; // including default case + assert currentBlock.normalSuccessors == nofCases; + + int[] keys = new int[nofCases - 1]; + for (int i = 0; i < nofCases - 1; ++i) { + keys[i] = ls.keyAt(i); + } + LookupSwitchNode lookupSwitch = currentGraph.add(new LookupSwitchNode(value, keys, switchProbability(nofCases, bci))); + for (int i = 0; i < nofCases; ++i) { + lookupSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); + } + append(lookupSwitch); + } + + private ConstantNode appendConstant(CiConstant constant) { + return ConstantNode.forCiConstant(constant, runtime, currentGraph); + } + + private ValueNode append(FixedNode fixed) { + lastInstr.setNext(fixed); + lastInstr = null; + return fixed; + } + + private ValueNode append(FixedWithNextNode x) { + return appendWithBCI(x); + } + + private static ValueNode append(ValueNode v) { + return v; + } + + private ValueNode appendWithBCI(FixedWithNextNode x) { + assert x.predecessor() == null : "instruction should not have been appended yet"; + assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")"; + lastInstr.setNext(x); + lastInstr = x; + return x; + } + + private FixedNode createTarget(Block block, FrameStateAccess stateAfter) { + assert block != null && stateAfter != null; + assert block.isLoopHeader || block.firstInstruction == null || block.firstInstruction.next() == null : + "non-loop block must be iterated after all its predecessors. startBci=" + block.startBci + ", " + block.getClass().getSimpleName() + ", " + block.firstInstruction.next(); + + if (block.isExceptionEntry) { + assert stateAfter.stackSize() == 1; + } + + if (block.firstInstruction == null) { + if (block.isLoopHeader) { + LoopBeginNode loopBegin = currentGraph.add(new LoopBeginNode()); + loopBegin.addEnd(currentGraph.add(new EndNode())); + LoopEndNode loopEnd = currentGraph.add(new LoopEndNode()); + loopEnd.setLoopBegin(loopBegin); + PlaceholderNode pBegin = currentGraph.add(new PlaceholderNode()); + pBegin.setNext(loopBegin.forwardEdge()); + PlaceholderNode pEnd = currentGraph.add(new PlaceholderNode()); + pEnd.setNext(loopEnd); + loopBegin.setStateAfter(stateAfter.duplicate(block.startBci)); + block.firstInstruction = pBegin; + } else { + block.firstInstruction = currentGraph.add(new PlaceholderNode()); + } + } + mergeOrClone(block, stateAfter); + addToWorkList(block); + + FixedNode result = null; + if (block.isLoopHeader && isVisited(block)) { + result = (FixedNode) loopBegin(block).loopEnd().predecessor(); + } else { + result = block.firstInstruction; + } + + assert result instanceof MergeNode || result instanceof PlaceholderNode : result; + if (result instanceof MergeNode) { + if (result instanceof LoopBeginNode) { + result = ((LoopBeginNode) result).forwardEdge(); + } else { + EndNode end = currentGraph.add(new EndNode()); + ((MergeNode) result).addEnd(end); + PlaceholderNode p = currentGraph.add(new PlaceholderNode()); + int bci = block.startBci; + if (block instanceof ExceptionBlock) { + bci = ((ExceptionBlock) block).deoptBci; + } + p.setStateAfter(stateAfter.duplicate(bci)); + p.setNext(end); + result = p; + } + } + assert !(result instanceof LoopBeginNode || result instanceof MergeNode); + return result; + } + + private ValueNode synchronizedObject(FrameStateAccess state, RiResolvedMethod target) { + if (isStatic(target.accessFlags())) { + return append(ConstantNode.forCiConstant(target.holder().getEncoding(Representation.JavaClass), runtime, currentGraph)); + } else { + return state.localAt(0); + } + } + + private void iterateAllBlocks() { + Block block; + while ((block = removeFromWorkList()) != null) { + // remove blocks that have no predecessors by the time it their bytecodes are parsed + if (block.firstInstruction == null) { + markVisited(block); + continue; + } + + if (!isVisited(block)) { + markVisited(block); + // now parse the block + if (block.isLoopHeader) { + LoopBeginNode begin = loopBegin(block); + FrameState preLoopState = ((StateSplit) block.firstInstruction).stateAfter(); + assert preLoopState != null; + FrameState duplicate = preLoopState.duplicate(preLoopState.bci); + begin.setStateAfter(duplicate); + duplicate.insertLoopPhis(begin); + lastInstr = begin; + } else { + lastInstr = block.firstInstruction; + } + frameState.initializeFrom(((StateSplit) lastInstr).stateAfter()); + assert lastInstr.next() == null : "instructions already appended at block " + block.blockID; + + if (block == returnBlock) { + createReturn(); + } else if (block == unwindBlock) { + createUnwind(); + } else if (block instanceof ExceptionBlock) { + createExceptionDispatch((ExceptionBlock) block); + } else if (block instanceof DeoptBlock) { + createDeopt(); + } else { + frameState.setRethrowException(false); + iterateBytecodesForBlock(block); + } + } + } + } + + private void connectLoopEndToBegin() { + for (LoopBeginNode begin : currentGraph.getNodes(LoopBeginNode.class)) { + LoopEndNode loopEnd = begin.loopEnd(); + AbstractStateSplit loopEndStateSplit = (AbstractStateSplit) loopEnd.predecessor(); + if (loopEndStateSplit.stateAfter() != null) { + begin.stateAfter().mergeLoop(begin, loopEndStateSplit.stateAfter()); + } else { +// This can happen with degenerated loops like this one: +// for (;;) { +// try { +// break; +// } catch (UnresolvedException iioe) { +// } +// } + // Delete the phis (all of them must have exactly one input). + for (PhiNode phi : begin.phis().snapshot()) { + assert phi.valueCount() == 1; + begin.stateAfter().deleteRedundantPhi(phi, phi.firstValue()); + } + + // Delete the loop end. + loopEndStateSplit.safeDelete(); + loopEnd.safeDelete(); + + // Remove the loop begin. + EndNode loopEntryEnd = begin.forwardEdge(); + FixedNode beginSucc = begin.next(); + FrameState stateAfter = begin.stateAfter(); + stateAfter.delete(); + begin.safeDelete(); + loopEntryEnd.replaceAndDelete(beginSucc); + } + } + } + + private static LoopBeginNode loopBegin(Block block) { + EndNode endNode = (EndNode) block.firstInstruction.next(); + LoopBeginNode loopBegin = (LoopBeginNode) endNode.merge(); + return loopBegin; + } + + private void createDeopt() { + append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile))); + } + + private void createUnwind() { + synchronizedEpilogue(FrameState.AFTER_EXCEPTION_BCI); + UnwindNode unwindNode = currentGraph.add(new UnwindNode(frameState.apop())); + append(unwindNode); + } + + private void createReturn() { + if (method.isConstructor() && method.holder().superType() == null) { + callRegisterFinalizer(); + } + CiKind returnKind = method.signature().returnKind(false).stackKind(); + ValueNode x = returnKind == CiKind.Void ? null : frameState.pop(returnKind); + assert frameState.stackSize() == 0; + + // TODO (gd) remove this when FloatingRead is fixed + if (Modifier.isSynchronized(method.accessFlags())) { + append(currentGraph.add(new ValueAnchorNode(x))); + } + + synchronizedEpilogue(FrameState.AFTER_BCI); + ReturnNode returnNode = currentGraph.add(new ReturnNode(x)); + append(returnNode); + } + + private void synchronizedEpilogue(int bci) { + if (Modifier.isSynchronized(method.accessFlags())) { + MonitorExitNode monitorExit = genMonitorExit(methodSynchronizedObject); + monitorExit.setStateAfter(frameState.create(bci)); + } + } + + 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; + + RiType catchType = block.handler.catchType(); + ConstantNode typeInstruction = genTypeOrDeopt(RiType.Representation.ObjectHub, catchType, (catchType instanceof RiResolvedType) && ((RiResolvedType) catchType).isInitialized()); + 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); + } + } + } + + private void appendGoto(FixedNode target) { + if (lastInstr != null) { + lastInstr.setNext(target); + } + } + + private void iterateBytecodesForBlock(Block block) { + assert frameState != null; + + currentBlock = block; + + int endBCI = stream.endBCI(); + + stream.setBCI(block.startBci); + int bci = block.startBci; + while (bci < endBCI) { + // read the opcode + int opcode = stream.currentBC(); + traceState(); + traceInstruction(bci, opcode, bci == block.startBci); + processBytecode(bci, opcode); + + if (lastInstr == null || IdentifyBlocksPhase.isBlockEnd(lastInstr) || lastInstr.next() != null) { + break; + } + + stream.next(); + bci = stream.currentBCI(); + if (lastInstr instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) lastInstr; + if (stateSplit.stateAfter() == null && stateSplit.needsStateAfter()) { + stateSplit.setStateAfter(frameState.create(bci)); + } + } + if (bci < endBCI) { + if (bci > block.endBci) { + assert !block.successors.get(0).isExceptionEntry; + assert block.normalSuccessors == 1; + // we fell through to the next block, add a goto and break + appendGoto(createTarget(block.successors.get(0), frameState)); + break; + } + } + } + } + + private void traceState() { + if (GraalOptions.TraceBytecodeParserLevel >= TRACELEVEL_STATE && !TTY.isSuppressed()) { + log.println(String.format("| state [nr locals = %d, stack depth = %d, method = %s]", frameState.localsSize(), frameState.stackSize(), method)); + for (int i = 0; i < frameState.localsSize(); ++i) { + ValueNode value = frameState.localAt(i); + log.println(String.format("| local[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < frameState.stackSize(); ++i) { + ValueNode value = frameState.stackAt(i); + log.println(String.format("| stack[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < frameState.locksSize(); ++i) { + ValueNode value = frameState.lockAt(i); + log.println(String.format("| lock[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value)); + } + } + } + + private void processBytecode(int bci, int opcode) { + int cpi; + + // Checkstyle: stop + switch (opcode) { + case NOP : /* nothing to do */ break; + case ACONST_NULL : frameState.apush(appendConstant(CiConstant.NULL_OBJECT)); break; + case ICONST_M1 : frameState.ipush(appendConstant(CiConstant.INT_MINUS_1)); break; + case ICONST_0 : frameState.ipush(appendConstant(CiConstant.INT_0)); break; + case ICONST_1 : frameState.ipush(appendConstant(CiConstant.INT_1)); break; + case ICONST_2 : frameState.ipush(appendConstant(CiConstant.INT_2)); break; + case ICONST_3 : frameState.ipush(appendConstant(CiConstant.INT_3)); break; + case ICONST_4 : frameState.ipush(appendConstant(CiConstant.INT_4)); break; + case ICONST_5 : frameState.ipush(appendConstant(CiConstant.INT_5)); break; + case LCONST_0 : frameState.lpush(appendConstant(CiConstant.LONG_0)); break; + case LCONST_1 : frameState.lpush(appendConstant(CiConstant.LONG_1)); break; + case FCONST_0 : frameState.fpush(appendConstant(CiConstant.FLOAT_0)); break; + case FCONST_1 : frameState.fpush(appendConstant(CiConstant.FLOAT_1)); break; + case FCONST_2 : frameState.fpush(appendConstant(CiConstant.FLOAT_2)); break; + case DCONST_0 : frameState.dpush(appendConstant(CiConstant.DOUBLE_0)); break; + case DCONST_1 : frameState.dpush(appendConstant(CiConstant.DOUBLE_1)); break; + case BIPUSH : frameState.ipush(appendConstant(CiConstant.forInt(stream.readByte()))); break; + case SIPUSH : frameState.ipush(appendConstant(CiConstant.forInt(stream.readShort()))); break; + case LDC : // fall through + case LDC_W : // fall through + case LDC2_W : genLoadConstant(stream.readCPI()); break; + case ILOAD : loadLocal(stream.readLocalIndex(), CiKind.Int); break; + case LLOAD : loadLocal(stream.readLocalIndex(), CiKind.Long); break; + case FLOAD : loadLocal(stream.readLocalIndex(), CiKind.Float); break; + case DLOAD : loadLocal(stream.readLocalIndex(), CiKind.Double); break; + case ALOAD : loadLocal(stream.readLocalIndex(), CiKind.Object); break; + case ILOAD_0 : // fall through + case ILOAD_1 : // fall through + case ILOAD_2 : // fall through + case ILOAD_3 : loadLocal(opcode - ILOAD_0, CiKind.Int); break; + case LLOAD_0 : // fall through + case LLOAD_1 : // fall through + case LLOAD_2 : // fall through + case LLOAD_3 : loadLocal(opcode - LLOAD_0, CiKind.Long); break; + case FLOAD_0 : // fall through + case FLOAD_1 : // fall through + case FLOAD_2 : // fall through + case FLOAD_3 : loadLocal(opcode - FLOAD_0, CiKind.Float); break; + case DLOAD_0 : // fall through + case DLOAD_1 : // fall through + case DLOAD_2 : // fall through + case DLOAD_3 : loadLocal(opcode - DLOAD_0, CiKind.Double); break; + case ALOAD_0 : // fall through + case ALOAD_1 : // fall through + case ALOAD_2 : // fall through + case ALOAD_3 : loadLocal(opcode - ALOAD_0, CiKind.Object); break; + case IALOAD : genLoadIndexed(CiKind.Int ); break; + case LALOAD : genLoadIndexed(CiKind.Long ); break; + case FALOAD : genLoadIndexed(CiKind.Float ); break; + case DALOAD : genLoadIndexed(CiKind.Double); break; + case AALOAD : genLoadIndexed(CiKind.Object); break; + case BALOAD : genLoadIndexed(CiKind.Byte ); break; + case CALOAD : genLoadIndexed(CiKind.Char ); break; + case SALOAD : genLoadIndexed(CiKind.Short ); break; + case ISTORE : storeLocal(CiKind.Int, stream.readLocalIndex()); break; + case LSTORE : storeLocal(CiKind.Long, stream.readLocalIndex()); break; + case FSTORE : storeLocal(CiKind.Float, stream.readLocalIndex()); break; + case DSTORE : storeLocal(CiKind.Double, stream.readLocalIndex()); break; + case ASTORE : storeLocal(CiKind.Object, stream.readLocalIndex()); break; + case ISTORE_0 : // fall through + case ISTORE_1 : // fall through + case ISTORE_2 : // fall through + case ISTORE_3 : storeLocal(CiKind.Int, opcode - ISTORE_0); break; + case LSTORE_0 : // fall through + case LSTORE_1 : // fall through + case LSTORE_2 : // fall through + case LSTORE_3 : storeLocal(CiKind.Long, opcode - LSTORE_0); break; + case FSTORE_0 : // fall through + case FSTORE_1 : // fall through + case FSTORE_2 : // fall through + case FSTORE_3 : storeLocal(CiKind.Float, opcode - FSTORE_0); break; + case DSTORE_0 : // fall through + case DSTORE_1 : // fall through + case DSTORE_2 : // fall through + case DSTORE_3 : storeLocal(CiKind.Double, opcode - DSTORE_0); break; + case ASTORE_0 : // fall through + case ASTORE_1 : // fall through + case ASTORE_2 : // fall through + case ASTORE_3 : storeLocal(CiKind.Object, opcode - ASTORE_0); break; + case IASTORE : genStoreIndexed(CiKind.Int ); break; + case LASTORE : genStoreIndexed(CiKind.Long ); break; + case FASTORE : genStoreIndexed(CiKind.Float ); break; + case DASTORE : genStoreIndexed(CiKind.Double); break; + case AASTORE : genStoreIndexed(CiKind.Object); break; + case BASTORE : genStoreIndexed(CiKind.Byte ); break; + case CASTORE : genStoreIndexed(CiKind.Char ); break; + case SASTORE : genStoreIndexed(CiKind.Short ); break; + case POP : // fall through + case POP2 : // fall through + case DUP : // fall through + case DUP_X1 : // fall through + case DUP_X2 : // fall through + case DUP2 : // fall through + case DUP2_X1 : // fall through + case DUP2_X2 : // fall through + case SWAP : stackOp(opcode); break; + case IADD : // fall through + case ISUB : // fall through + case IMUL : genArithmeticOp(CiKind.Int, opcode, false); break; + case IDIV : // fall through + case IREM : genArithmeticOp(CiKind.Int, opcode, true); break; + case LADD : // fall through + case LSUB : // fall through + case LMUL : genArithmeticOp(CiKind.Long, opcode, false); break; + case LDIV : // fall through + case LREM : genArithmeticOp(CiKind.Long, opcode, true); break; + case FADD : // fall through + case FSUB : // fall through + case FMUL : // fall through + case FDIV : // fall through + case FREM : genArithmeticOp(CiKind.Float, opcode, false); break; + case DADD : // fall through + case DSUB : // fall through + case DMUL : // fall through + case DDIV : // fall through + case DREM : genArithmeticOp(CiKind.Double, opcode, false); break; + case INEG : genNegateOp(CiKind.Int); break; + case LNEG : genNegateOp(CiKind.Long); break; + case FNEG : genNegateOp(CiKind.Float); break; + case DNEG : genNegateOp(CiKind.Double); break; + case ISHL : // fall through + case ISHR : // fall through + case IUSHR : genShiftOp(CiKind.Int, opcode); break; + case IAND : // fall through + case IOR : // fall through + case IXOR : genLogicOp(CiKind.Int, opcode); break; + case LSHL : // fall through + case LSHR : // fall through + case LUSHR : genShiftOp(CiKind.Long, opcode); break; + case LAND : // fall through + case LOR : // fall through + case LXOR : genLogicOp(CiKind.Long, opcode); break; + case IINC : genIncrement(); break; + case I2L : genConvert(ConvertNode.Op.I2L); break; + case I2F : genConvert(ConvertNode.Op.I2F); break; + case I2D : genConvert(ConvertNode.Op.I2D); break; + case L2I : genConvert(ConvertNode.Op.L2I); break; + case L2F : genConvert(ConvertNode.Op.L2F); break; + case L2D : genConvert(ConvertNode.Op.L2D); break; + case F2I : genConvert(ConvertNode.Op.F2I); break; + case F2L : genConvert(ConvertNode.Op.F2L); break; + case F2D : genConvert(ConvertNode.Op.F2D); break; + case D2I : genConvert(ConvertNode.Op.D2I); break; + case D2L : genConvert(ConvertNode.Op.D2L); break; + case D2F : genConvert(ConvertNode.Op.D2F); break; + case I2B : genConvert(ConvertNode.Op.I2B); break; + case I2C : genConvert(ConvertNode.Op.I2C); break; + case I2S : genConvert(ConvertNode.Op.I2S); break; + case LCMP : genCompareOp(CiKind.Long, false); break; + case FCMPL : genCompareOp(CiKind.Float, true); break; + case FCMPG : genCompareOp(CiKind.Float, false); break; + case DCMPL : genCompareOp(CiKind.Double, true); break; + case DCMPG : genCompareOp(CiKind.Double, false); break; + case IFEQ : genIfZero(Condition.EQ); break; + case IFNE : genIfZero(Condition.NE); break; + case IFLT : genIfZero(Condition.LT); break; + case IFGE : genIfZero(Condition.GE); break; + case IFGT : genIfZero(Condition.GT); break; + case IFLE : genIfZero(Condition.LE); break; + case IF_ICMPEQ : genIfSame(CiKind.Int, Condition.EQ); break; + case IF_ICMPNE : genIfSame(CiKind.Int, Condition.NE); break; + case IF_ICMPLT : genIfSame(CiKind.Int, Condition.LT); break; + case IF_ICMPGE : genIfSame(CiKind.Int, Condition.GE); break; + case IF_ICMPGT : genIfSame(CiKind.Int, Condition.GT); break; + case IF_ICMPLE : genIfSame(CiKind.Int, Condition.LE); break; + case IF_ACMPEQ : genIfSame(CiKind.Object, Condition.EQ); break; + case IF_ACMPNE : genIfSame(CiKind.Object, Condition.NE); break; + case GOTO : genGoto(); break; + case JSR : genJsr(stream.readBranchDest()); break; + case RET : genRet(stream.readLocalIndex()); break; + case TABLESWITCH : genTableswitch(); break; + case LOOKUPSWITCH : genLookupswitch(); break; + case IRETURN : genReturn(frameState.ipop()); break; + case LRETURN : genReturn(frameState.lpop()); break; + case FRETURN : genReturn(frameState.fpop()); break; + case DRETURN : genReturn(frameState.dpop()); break; + case ARETURN : genReturn(frameState.apop()); break; + case RETURN : genReturn(null); break; + case GETSTATIC : cpi = stream.readCPI(); genGetStatic(lookupField(cpi, opcode)); break; + case PUTSTATIC : cpi = stream.readCPI(); genPutStatic(lookupField(cpi, opcode)); break; + case GETFIELD : cpi = stream.readCPI(); genGetField(lookupField(cpi, opcode)); break; + case PUTFIELD : cpi = stream.readCPI(); genPutField(lookupField(cpi, opcode)); break; + case INVOKEVIRTUAL : cpi = stream.readCPI(); genInvokeVirtual(lookupMethod(cpi, opcode)); break; + case INVOKESPECIAL : cpi = stream.readCPI(); genInvokeSpecial(lookupMethod(cpi, opcode)); break; + case INVOKESTATIC : cpi = stream.readCPI(); genInvokeStatic(lookupMethod(cpi, opcode)); break; + case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(lookupMethod(cpi, opcode)); break; + case NEW : genNewInstance(stream.readCPI()); break; + case NEWARRAY : genNewTypeArray(stream.readLocalIndex()); break; + case ANEWARRAY : genNewObjectArray(stream.readCPI()); break; + case ARRAYLENGTH : genArrayLength(); break; + case ATHROW : genThrow(stream.currentBCI()); break; + case CHECKCAST : genCheckCast(); break; + case INSTANCEOF : genInstanceOf(); break; + case MONITORENTER : genMonitorEnter(frameState.apop()); break; + case MONITOREXIT : genMonitorExit(frameState.apop()); break; + case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break; + case IFNULL : genIfNull(Condition.EQ); break; + case IFNONNULL : genIfNull(Condition.NE); break; + case GOTO_W : genGoto(); break; + case JSR_W : genJsr(stream.readFarBranchDest()); break; + case BREAKPOINT: + throw new CiBailout("concurrent setting of breakpoint"); + default: + throw new CiBailout("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]"); + } + // Checkstyle: resume + } + + private void traceInstruction(int bci, int opcode, boolean blockStart) { + if (GraalOptions.TraceBytecodeParserLevel >= TRACELEVEL_INSTRUCTIONS && !TTY.isSuppressed()) { + StringBuilder sb = new StringBuilder(40); + sb.append(blockStart ? '+' : '|'); + if (bci < 10) { + sb.append(" "); + } else if (bci < 100) { + sb.append(' '); + } + sb.append(bci).append(": ").append(Bytecodes.nameOf(opcode)); + for (int i = bci + 1; i < stream.nextBCI(); ++i) { + sb.append(' ').append(stream.readUByte(i)); + } + if (!currentBlock.jsrScope.isEmpty()) { + sb.append(' ').append(currentBlock.jsrScope); + } + log.println(sb.toString()); + } + } + + private void genArrayLength() { + frameState.ipush(append(currentGraph.add(new ArrayLengthNode(frameState.apop())))); + } + + /** + * Adds a block to the worklist, if it is not already in the worklist. + * This method will keep the worklist topologically stored (i.e. the lower + * DFNs are earlier in the list). + * @param block the block to add to the work list + */ + private void addToWorkList(Block block) { + if (!isOnWorkList(block)) { + markOnWorkList(block); + sortIntoWorkList(block); + } + } + + private void sortIntoWorkList(Block top) { + workList.offer(top); + } + + /** + * Removes the next block from the worklist. The list is sorted topologically, so the + * block with the lowest depth first number in the list will be removed and returned. + * @return the next block from the worklist; {@code null} if there are no blocks + * in the worklist + */ + private Block removeFromWorkList() { + return workList.poll(); + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrNotSupportedBailout.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrNotSupportedBailout.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +import com.oracle.max.cri.ci.*; + + +public class JsrNotSupportedBailout extends CiBailout{ + private static final long serialVersionUID = -7476925652727154272L; + + public JsrNotSupportedBailout(String reason) { + super(reason); + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrScope.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrScope.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.java; + +public class JsrScope { + + public static final JsrScope EMPTY_SCOPE = new JsrScope(); + + private final long scope; + + private JsrScope(long scope) { + this.scope = scope; + } + + public JsrScope() { + this.scope = 0; + } + + public int nextReturnAddress() { + return (int) (scope & 0xffff); + } + + public JsrScope push(int jsrReturnBci) { + if ((scope & 0xffff000000000000L) != 0) { + throw new JsrNotSupportedBailout("only four jsr nesting levels are supported"); + } + return new JsrScope((scope << 16) | jsrReturnBci); + } + + public boolean isEmpty() { + return scope == 0; + } + + public JsrScope pop() { + return new JsrScope(scope >>> 16); + } + + @Override + public int hashCode() { + return (int) (scope ^ (scope >>> 32)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + return obj != null && getClass() == obj.getClass() && scope == ((JsrScope) obj).scope; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + long tmp = scope; + sb.append(" ["); + while (tmp != 0) { + sb.append(", ").append(tmp & 0xffff); + tmp = tmp >>> 16; + } + sb.append(']'); + return sb.toString(); + } +} diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/package-info.java Tue Jan 03 18:22:10 2012 +0100 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + */ +package com.oracle.max.graal.java; diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java --- a/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java Tue Jan 03 18:22:10 2012 +0100 @@ -33,12 +33,12 @@ import com.oracle.max.graal.compiler.*; import com.oracle.max.graal.compiler.alloc.*; import com.oracle.max.graal.compiler.alloc.Interval.UsePosList; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.lir.*; import com.oracle.max.graal.compiler.schedule.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.graph.Node.*; import com.oracle.max.graal.graph.NodeClass.*; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.calc.*; diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinterObserver.java --- a/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinterObserver.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinterObserver.java Tue Jan 03 18:22:10 2012 +0100 @@ -30,11 +30,11 @@ import com.oracle.max.criutils.*; import com.oracle.max.graal.compiler.*; import com.oracle.max.graal.compiler.alloc.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.lir.*; import com.oracle.max.graal.compiler.observer.*; import com.oracle.max.graal.compiler.schedule.*; import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.nodes.*; /** diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinter.java --- a/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinter.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinter.java Tue Jan 03 18:22:10 2012 +0100 @@ -28,7 +28,6 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.schedule.*; import com.oracle.max.graal.compiler.util.*; import com.oracle.max.graal.compiler.util.LoopUtil.Loop; @@ -36,6 +35,7 @@ import com.oracle.max.graal.graph.Node.Verbosity; import com.oracle.max.graal.graph.NodeClass.NodeClassIterator; import com.oracle.max.graal.graph.NodeClass.Position; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.loop.*; import com.oracle.max.graal.printer.BasicIdealGraphPrinter.*; diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.snippets/src/com/oracle/max/graal/snippets/Snippets.java --- a/graal/com.oracle.max.graal.snippets/src/com/oracle/max/graal/snippets/Snippets.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.snippets/src/com/oracle/max/graal/snippets/Snippets.java Tue Jan 03 18:22:10 2012 +0100 @@ -27,12 +27,12 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.observer.*; import com.oracle.max.graal.compiler.phases.*; import com.oracle.max.graal.compiler.util.*; import com.oracle.max.graal.cri.*; import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.extended.*; import com.oracle.max.graal.nodes.java.*; diff -r 4df4499e0289 -r 2af849af1723 graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/GraphTest.java --- a/graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/GraphTest.java Tue Jan 03 17:53:26 2012 +0100 +++ b/graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/GraphTest.java Tue Jan 03 18:22:10 2012 +0100 @@ -30,10 +30,10 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.graal.compiler.*; -import com.oracle.max.graal.compiler.graphbuilder.*; import com.oracle.max.graal.compiler.phases.*; import com.oracle.max.graal.compiler.phases.PhasePlan.*; import com.oracle.max.graal.cri.*; +import com.oracle.max.graal.java.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.printer.*; diff -r 4df4499e0289 -r 2af849af1723 mx/projects --- a/mx/projects Tue Jan 03 17:53:26 2012 +0100 +++ b/mx/projects Tue Jan 03 18:22:10 2012 +0100 @@ -70,10 +70,16 @@ project@com.oracle.max.graal.compiler@dependencies=com.oracle.max.asm,com.oracle.max.graal.nodes project@com.oracle.max.graal.compiler@checkstyle=com.oracle.max.graal.graph +# graal.java +project@com.oracle.max.graal.java@subDir=graal +project@com.oracle.max.graal.java@sourceDirs=src +project@com.oracle.max.graal.java@dependencies=com.oracle.max.graal.compiler +project@com.oracle.max.graal.java@checkstyle=com.oracle.max.graal.graph + # graal.printer project@com.oracle.max.graal.printer@subDir=graal project@com.oracle.max.graal.printer@sourceDirs=src -project@com.oracle.max.graal.printer@dependencies=com.oracle.max.graal.compiler +project@com.oracle.max.graal.printer@dependencies=com.oracle.max.graal.java project@com.oracle.max.graal.printer@checkstyle=com.oracle.max.graal.graph # graal.test diff -r 4df4499e0289 -r 2af849af1723 src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Tue Jan 03 17:53:26 2012 +0100 +++ b/src/share/vm/runtime/arguments.cpp Tue Jan 03 18:22:10 2012 +0100 @@ -2101,6 +2101,8 @@ prepend_to_graal_classpath(scp_compiler, graal_dir, "com.oracle.max.graal.nodes"); prepend_to_graal_classpath(scp_compiler, graal_dir, "com.oracle.max.graal.snippets"); prepend_to_graal_classpath(scp_compiler, graal_dir, "com.oracle.max.graal.hotspot"); + prepend_to_graal_classpath(scp_compiler, graal_dir, "com.oracle.max.graal.printer"); + prepend_to_graal_classpath(scp_compiler, graal_dir, "com.oracle.max.graal.java"); scp_compiler.expand_endorsed(); Arguments::set_compilerclasspath(scp_compiler.combined_path());