# HG changeset patch # User Christian Haeubl # Date 1331143353 28800 # Node ID d0d0dfbebd03d125a630ff8bb4372a9e8fecb206 # Parent e5d42eccfb29e4afcebf44d17546ad5e312b2fc2# Parent 44d746dc51bff61d9726d7ab7cf4f571a6aab72f Merge diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/XirSite.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/XirSite.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/XirSite.java Wed Mar 07 10:02:33 2012 -0800 @@ -22,8 +22,6 @@ */ package com.oracle.max.cri.xir; -import com.oracle.max.cri.ci.*; - /** * Encapsulates the notion of a site where XIR can be supplied. It is supplied to the {@link RiXirGenerator} by the * compiler for each place where XIR can be generated. This interface allows a number of queries, including the @@ -32,13 +30,6 @@ public interface XirSite { /** - * Gets the {@link CiCodePos code position} associated with this site. This is useful for inserting - * instrumentation at the XIR level. - * @return the code position if it is available; {@code null} otherwise - */ - CiCodePos getCodePos(); - - /** * Checks whether the specified argument is guaranteed to be non-null at this site. * @param argument the argument * @return {@code true} if the argument is non null at this site diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Wed Mar 07 10:02:33 2012 -0800 @@ -187,6 +187,7 @@ public static boolean OptReorderLoops = true; public static boolean OptEliminateGuards = true; public static boolean OptImplicitNullChecks = true; + public static boolean OptLivenessAnalysis = true; /** * Flag to turn on SSA-based register allocation, which is currently under development. diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java Wed Mar 07 10:02:33 2012 -0800 @@ -27,6 +27,7 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.graal.compiler.gen.LIRGenerator.LockScope; +import com.oracle.max.graal.debug.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.lir.*; import com.oracle.max.graal.nodes.*; @@ -106,13 +107,16 @@ } private CiFrame computeFrameForState(FrameState state, LockScope locks) { + int numLocals = state.localsSize(); + int numStack = state.stackSize(); int numLocks = (locks != null && locks.callerState == state.outerFrameState()) ? locks.stateDepth + 1 : 0; - CiValue[] values = new CiValue[state.valuesSize() + numLocks]; - int valueIndex = 0; - - for (int i = 0; i < state.valuesSize(); i++) { - values[valueIndex++] = toCiValue(state.valueAt(i)); + CiValue[] values = new CiValue[numLocals + numStack + numLocks]; + for (int i = 0; i < numLocals; i++) { + values[i] = toCiValue(state.localAt(i)); + } + for (int i = 0; i < numStack; i++) { + values[numLocals + i] = toCiValue(state.stackAt(i)); } LockScope nextLock = locks; @@ -122,7 +126,7 @@ CiValue owner = toCiValue(nextLock.monitor.object()); CiValue lockData = nextLock.lockData; boolean eliminated = nextLock.monitor.eliminated(); - values[state.valuesSize() + nextLock.stateDepth] = new CiMonitorValue(owner, lockData, eliminated); + values[numLocals + numStack + nextLock.stateDepth] = new CiMonitorValue(owner, lockData, eliminated); nextLock = nextLock.outer; } @@ -148,18 +152,22 @@ ciObj = CiVirtualObject.get(obj.type(), null, virtualObjects.size()); virtualObjects.put(obj, ciObj); } + Debug.metric("StateVirtualObjects").increment(); return ciObj; } else if (value instanceof ConstantNode) { + Debug.metric("StateConstants").increment(); return ((ConstantNode) value).value; } else if (value != null) { + Debug.metric("StateVariables").increment(); CiValue operand = nodeOperands.get(value); assert operand != null && (operand instanceof Variable || operand instanceof CiConstant); return operand; } else { // return a dummy value because real value not needed + Debug.metric("StateIllegals").increment(); return CiValue.IllegalValue; } } diff -r e5d42eccfb29 -r d0d0dfbebd03 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 Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Wed Mar 07 10:02:33 2012 -0800 @@ -28,7 +28,6 @@ import static com.oracle.max.cri.util.MemoryBarriers.*; import static com.oracle.max.graal.lir.ValueUtil.*; -import java.lang.reflect.*; import java.util.*; import com.oracle.max.asm.*; @@ -50,7 +49,11 @@ import com.oracle.max.graal.debug.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.lir.*; -import com.oracle.max.graal.lir.StandardOp.*; +import com.oracle.max.graal.lir.StandardOp.JumpOp; +import com.oracle.max.graal.lir.StandardOp.LabelOp; +import com.oracle.max.graal.lir.StandardOp.ParametersOp; +import com.oracle.max.graal.lir.StandardOp.PhiJumpOp; +import com.oracle.max.graal.lir.StandardOp.PhiLabelOp; import com.oracle.max.graal.lir.cfg.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.DeoptimizeNode.DeoptAction; @@ -347,7 +350,7 @@ } else { TTY.println("STATE CHANGE (singlePred)"); if (GraalOptions.TraceLIRGeneratorLevel >= 3) { - TTY.println(fs.toDetailedString()); + TTY.println(fs.toString(Node.Verbosity.Debugger)); } } } @@ -395,12 +398,11 @@ } if (stateAfter != null) { lastState = stateAfter; - assert checkStartOperands(instr, lastState); assert checkStateReady(lastState); if (GraalOptions.TraceLIRGeneratorLevel >= 2) { TTY.println("STATE CHANGE"); if (GraalOptions.TraceLIRGeneratorLevel >= 3) { - TTY.println(stateAfter.toDetailedString()); + TTY.println(stateAfter.toString(Node.Verbosity.Debugger)); } } } @@ -428,8 +430,7 @@ private boolean checkStateReady(FrameState state) { FrameState fs = state; while (fs != null) { - for (int i = 0; i < fs.valuesSize(); i++) { - ValueNode v = fs.valueAt(i); + for (ValueNode v : fs.values()) { if (v != null && !(v instanceof VirtualObjectNode)) { assert operand(v) != null : "Value " + v + " in " + fs + " is not ready!"; } @@ -492,25 +493,6 @@ } } - private boolean checkStartOperands(Node node, FrameState fs) { - if (!Modifier.isNative(method.accessFlags())) { - if (node == ((StructuredGraph) node.graph()).start()) { - CiKind[] arguments = CiUtil.signatureToKinds(method); - int slot = 0; - for (CiKind kind : arguments) { - ValueNode arg = fs.localAt(slot); - assert arg != null && arg.kind() == kind.stackKind() : "No valid local in framestate for slot #" + slot + " (" + arg + ")"; - slot++; - if (slot < fs.localsSize() && fs.localAt(slot) == null) { - slot++; - } - } - } - } - return true; - } - - @Override public void visitArrayLength(ArrayLengthNode x) { XirArgument array = toXirArgument(x.array()); @@ -1054,7 +1036,15 @@ FrameState stateAfter = x.stateAfter(); if (stateAfter != null) { // TODO change back to stateBeforeReturn() when RuntimeCallNode uses a CallTargetNode - FrameState stateBeforeReturn = stateAfter.duplicateModified(stateAfter.bci, stateAfter.rethrowException(), x.kind()); + // (cwi) I made the code that modifies the operand stack conditional. My scenario: runtime calls to, e.g., + // CreateNullPointerException have no equivalent in the bytecodes, so there is in invoke bytecode. + // Therefore, the result of the runtime call was never pushed to the stack, and we cannot pop it here. + FrameState stateBeforeReturn = stateAfter; + if ((stateAfter.stackSize() > 0 && stateAfter.stackAt(stateAfter.stackSize() - 1) == x) || + (stateAfter.stackSize() > 1 && stateAfter.stackAt(stateAfter.stackSize() - 2) == x)) { + + stateBeforeReturn = stateAfter.duplicateModified(stateAfter.bci, stateAfter.rethrowException(), x.kind()); + } // TODO is it correct here that the pointerSlots are not passed to the oop map generation? info = stateFor(stateBeforeReturn); @@ -1451,19 +1441,6 @@ ValueNode current; ValueNode receiver; - XirSupport() { - } - - public CiCodePos getCodePos() { - if (current instanceof StateSplit) { - FrameState stateAfter = ((StateSplit) current).stateAfter(); - if (stateAfter != null) { - return stateAfter.toCodePos(); - } - } - return null; - } - public boolean isNonNull(XirArgument argument) { return false; } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InsertStateAfterPlaceholderPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InsertStateAfterPlaceholderPhase.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InsertStateAfterPlaceholderPhase.java Wed Mar 07 10:02:33 2012 -0800 @@ -22,10 +22,24 @@ */ package com.oracle.max.graal.compiler.phases; +import com.oracle.max.graal.graph.*; import com.oracle.max.graal.nodes.*; +import com.oracle.max.graal.nodes.spi.*; +import com.oracle.max.graal.nodes.type.*; public class InsertStateAfterPlaceholderPhase extends Phase { + private static class PlaceholderNode extends AbstractStateSplit implements Node.IterableNodeType, LIRLowerable { + public PlaceholderNode() { + super(StampFactory.illegal()); + } + + @Override + public void generate(LIRGeneratorTool gen) { + // nothing to do + } + } + @Override protected void run(StructuredGraph graph) { for (ReturnNode ret : graph.getNodes(ReturnNode.class)) { diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/Phase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/Phase.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/Phase.java Wed Mar 07 10:02:33 2012 -0800 @@ -55,6 +55,7 @@ if (dumpGraph) { Debug.dump(graph, "After phase %s", name); } + assert graph.verify(); } }); } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java Wed Mar 07 10:02:33 2012 -0800 @@ -39,7 +39,7 @@ private final String meterFilter; private final String timerFilter; private final String dumpFilter; - private final String methodFilter; + private final String[] methodFilter; private final List dumpHandlers = new ArrayList<>(); public HotSpotDebugConfig(String logFilter, String meterFilter, String timerFilter, String dumpFilter, String methodFilter) { @@ -47,7 +47,7 @@ this.meterFilter = meterFilter; this.timerFilter = timerFilter; this.dumpFilter = dumpFilter; - this.methodFilter = methodFilter; + this.methodFilter = methodFilter == null ? null : methodFilter.split(","); dumpHandlers.add(new IdealGraphPrinterDumpHandler(GraalOptions.PrintIdealGraphAddress, GraalOptions.PrintIdealGraphPort)); dumpHandlers.add(new CFGPrinterObserver()); } @@ -89,9 +89,11 @@ } else { for (Object o : Debug.context()) { if (o instanceof RiMethod) { - RiMethod riMethod = (RiMethod) o; - if (CiUtil.format("%H.%n", riMethod).contains(methodFilter)) { - return true; + String methodName = CiUtil.format("%H.%n", (RiMethod) o); + for (String filter : methodFilter) { + if (methodName.contains(filter)) { + return true; + } } } } @@ -107,7 +109,7 @@ add(sb, "Meter", meterFilter); add(sb, "Time", timerFilter); add(sb, "Dump", dumpFilter); - add(sb, "MethodFilter", methodFilter); + add(sb, "MethodFilter", Arrays.toString(methodFilter)); return sb.toString(); } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Wed Mar 07 10:02:33 2012 -0800 @@ -30,7 +30,7 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; import com.oracle.max.criutils.*; -import com.oracle.max.graal.java.*; +import com.oracle.max.graal.java.bytecode.*; /** * Implementation of RiMethod for resolved HotSpot methods. diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java Wed Mar 07 10:02:33 2012 -0800 @@ -437,7 +437,7 @@ private CiTargetMethod createCallbackStub(RiResolvedMethod method, CiGenericCallback callback) { StructuredGraph graph = new StructuredGraph(); - FrameStateBuilder frameState = new FrameStateBuilder(method, method.maxLocals(), method.maxStackSize(), graph, false); + FrameStateBuilder frameState = new FrameStateBuilder(method, graph, false); ValueNode local0 = frameState.loadLocal(0); FrameState initialFrameState = frameState.create(0); diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java Wed Mar 07 10:02:33 2012 -0800 @@ -22,13 +22,17 @@ */ package com.oracle.max.graal.java; -import static com.oracle.max.graal.java.Bytecodes.*; +import static com.oracle.max.graal.java.bytecode.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.debug.*; +import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.java.bytecode.*; import com.oracle.max.graal.nodes.*; /** @@ -121,6 +125,7 @@ public int blockID; public FixedWithNextNode firstInstruction; + public FrameStateBuilder entryState; public ArrayList successors = new ArrayList<>(2); public int normalSuccessors; @@ -136,6 +141,11 @@ public Block retSuccessor; public boolean endsWithRet = false; + public BitMap localsLiveIn; + public BitMap localsLiveOut; + private BitMap localsLiveGen; + private BitMap localsLiveKill; + public Block copy() { try { Block block = (Block) super.clone(); @@ -169,12 +179,6 @@ 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. */ @@ -182,6 +186,8 @@ public final RiResolvedMethod method; + private final BytecodeStream stream; + private final RiExceptionHandler[] exceptionHandlers; private Block[] blockMap; @@ -201,6 +207,7 @@ public BciBlockMapping(RiResolvedMethod method, boolean useBranchPrediction) { this.method = method; exceptionHandlers = method.exceptionHandlers(); + stream = new BytecodeStream(method.code()); this.blockMap = new Block[method.codeSize()]; this.canTrap = new BitSet(blockMap.length); this.blocks = new ArrayList<>(); @@ -232,6 +239,15 @@ // Discard big arrays so that they can be GCed blockMap = null; + + if (GraalOptions.OptLivenessAnalysis) { + Debug.scope("LivenessAnalysis", new Runnable() { + @Override + public void run() { + computeLiveness(); + } + }); + } } private void initializeBlockIds() { @@ -252,11 +268,12 @@ // 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(); RiProfilingInfo profilingInfo = method.profilingInfo(); Block current = null; - int bci = 0; - while (bci < code.length) { + stream.setBCI(0); + while (stream.currentBC() != Bytecodes.END) { + int bci = stream.currentBCI(); + if (current == null || blockMap[bci] != null) { Block b = makeBlock(bci); if (current != null) { @@ -267,8 +284,7 @@ blockMap[bci] = current; current.endBci = bci; - int opcode = Bytes.beU1(code, bci); - switch (opcode) { + switch (stream.currentBC()) { case IRETURN: // fall through case LRETURN: // fall through case FRETURN: // fall through @@ -300,43 +316,37 @@ case IFNULL: // fall through case IFNONNULL: { current = null; - double probability = useBranchPrediction ? profilingInfo.getBranchTakenProbability(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); + setSuccessors(bci, makeBlock(stream.readBranchDest()), makeBlock(stream.nextBCI())); 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); + setSuccessors(bci, makeBlock(stream.readBranchDest())); break; } case TABLESWITCH: { current = null; - BytecodeTableSwitch sw = new BytecodeTableSwitch(code, bci); + BytecodeTableSwitch sw = new BytecodeTableSwitch(stream, bci); setSuccessors(bci, makeSwitchSuccessors(sw)); break; } case LOOKUPSWITCH: { current = null; - BytecodeLookupSwitch sw = new BytecodeLookupSwitch(code, bci); + BytecodeLookupSwitch sw = new BytecodeLookupSwitch(stream, bci); setSuccessors(bci, makeSwitchSuccessors(sw)); break; } case JSR: case JSR_W: { hasJsrBytecodes = true; - int target = bci + Bytes.beSVar(code, bci + 1, opcode == JSR_W); + int target = stream.readBranchDest(); 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.jsrReturnBci = stream.nextBCI(); current = null; setSuccessors(bci, b1); break; @@ -346,66 +356,42 @@ 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); + setSuccessors(bci, makeBlock(stream.nextBCI())); canTrap.set(bci); break; } - default: { - if (canTrap(opcode, bci, profilingInfo)) { + 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 && profilingInfo.getExceptionSeen(bci) != RiExceptionSeen.FALSE) { canTrap.set(bci); } } } - bci += lengthOf(code, bci); + stream.next(); } } - private static boolean canTrap(int opcode, int bci, RiProfilingInfo profilingInfo) { - switch (opcode) { - 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 profilingInfo.getExceptionSeen(bci) != RiExceptionSeen.FALSE; - } - } - } - return false; - } - private Block makeBlock(int startBci) { Block oldBlock = blockMap[startBci]; if (oldBlock == null) { @@ -471,6 +457,7 @@ block.successors.add(block.retSuccessor); assert block.retSuccessor != block.jsrSuccessor; } + Debug.log("JSR alternatives block %s sux %s jsrSux %s retSux %s jsrScope %s", block, block.successors, block.jsrSuccessor, block.retSuccessor, block.jsrScope); if (block.jsrSuccessor != null || !scope.isEmpty()) { for (int i = 0; i < block.successors.size(); i++) { @@ -482,7 +469,7 @@ if (successor == block.retSuccessor) { nextScope = scope.pop(); } - if (!successor.jsrScope.isEmpty()) { + if (!successor.jsrScope.isPrefixOf(nextScope)) { throw new JsrNotSupportedBailout("unstructured control flow (" + successor.jsrScope + " " + nextScope + ")"); } if (!nextScope.isEmpty()) { @@ -640,4 +627,187 @@ return loops; } + + + private void computeLiveness() { + for (Block block : blocks) { + computeLocalLiveness(block); + } + + boolean changed; + int iteration = 0; + do { + Debug.log("Iteration %d", iteration); + changed = false; + for (int i = blocks.size() - 1; i >= 0; i--) { + Block block = blocks.get(i); + Debug.log(" start B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.blockID, block.startBci, block.endBci, block.localsLiveIn, block.localsLiveOut, block.localsLiveGen, block.localsLiveKill); + + boolean blockChanged = (iteration == 0); + for (Block sux : block.successors) { + Debug.log(" Successor B%d: %s", sux.blockID, sux.localsLiveIn); + blockChanged = block.localsLiveOut.setUnionWithResult(sux.localsLiveIn) || blockChanged; + } + + if (blockChanged) { + block.localsLiveIn.setFrom(block.localsLiveOut); + block.localsLiveIn.setDifference(block.localsLiveKill); + block.localsLiveIn.setUnion(block.localsLiveGen); + + for (Block sux : block.successors) { + if (sux instanceof ExceptionBlock) { + // Exception handler blocks can be reached from anywhere within the block jumping to them, + // so we conservatively assume local variables require by the exception handler are live both + // at the beginning and end of the block. + blockChanged = block.localsLiveIn.setUnionWithResult(sux.localsLiveIn) || blockChanged; + } + } + Debug.log(" end B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.blockID, block.startBci, block.endBci, block.localsLiveIn, block.localsLiveOut, block.localsLiveGen, block.localsLiveKill); + } + changed |= blockChanged; + } + iteration++; + } while (changed); + } + + private void computeLocalLiveness(Block block) { + block.localsLiveIn = new BitMap(method.maxLocals()); + block.localsLiveOut = new BitMap(method.maxLocals()); + block.localsLiveGen = new BitMap(method.maxLocals()); + block.localsLiveKill = new BitMap(method.maxLocals()); + + if (block.startBci < 0 || block.endBci < 0) { + return; + } + + stream.setBCI(block.startBci); + while (stream.currentBCI() <= block.endBci) { + switch (stream.currentBC()) { + case RETURN: + if (method.isConstructor() && method.holder().superType() == null) { + // return from Object.init implicitly registers a finalizer + // for the receiver if needed, so keep it alive. + loadOne(block, 0); + } + break; + + case LLOAD: + case DLOAD: + loadTwo(block, stream.readLocalIndex()); + break; + case LLOAD_0: + case DLOAD_0: + loadTwo(block, 0); + break; + case LLOAD_1: + case DLOAD_1: + loadTwo(block, 1); + break; + case LLOAD_2: + case DLOAD_2: + loadTwo(block, 2); + break; + case LLOAD_3: + case DLOAD_3: + loadTwo(block, 3); + break; + case ILOAD: + case IINC: + case FLOAD: + case ALOAD: + case RET: + loadOne(block, stream.readLocalIndex()); + break; + case ILOAD_0: + case FLOAD_0: + case ALOAD_0: + loadOne(block, 0); + break; + case ILOAD_1: + case FLOAD_1: + case ALOAD_1: + loadOne(block, 1); + break; + case ILOAD_2: + case FLOAD_2: + case ALOAD_2: + loadOne(block, 2); + break; + case ILOAD_3: + case FLOAD_3: + case ALOAD_3: + loadOne(block, 3); + break; + + case LSTORE: + case DSTORE: + storeTwo(block, stream.readLocalIndex()); + break; + case LSTORE_0: + case DSTORE_0: + storeTwo(block, 0); + break; + case LSTORE_1: + case DSTORE_1: + storeTwo(block, 1); + break; + case LSTORE_2: + case DSTORE_2: + storeTwo(block, 2); + break; + case LSTORE_3: + case DSTORE_3: + storeTwo(block, 3); + break; + case ISTORE: + case FSTORE: + case ASTORE: + storeOne(block, stream.readLocalIndex()); + break; + case ISTORE_0: + case FSTORE_0: + case ASTORE_0: + storeOne(block, 0); + break; + case ISTORE_1: + case FSTORE_1: + case ASTORE_1: + storeOne(block, 1); + break; + case ISTORE_2: + case FSTORE_2: + case ASTORE_2: + storeOne(block, 2); + break; + case ISTORE_3: + case FSTORE_3: + case ASTORE_3: + storeOne(block, 3); + break; + } + stream.next(); + } + } + + private static void loadTwo(Block block, int local) { + loadOne(block, local); + loadOne(block, local + 1); + } + + private static void loadOne(Block block, int local) { + if (!block.localsLiveKill.get(local)) { + block.localsLiveGen.set(local); + } + } + + private static void storeTwo(Block block, int local) { + storeOne(block, local); + storeOne(block, local + 1); + } + + private static void storeOne(Block block, int local) { + if (!block.localsLiveGen.get(local)) { + block.localsLiveKill.set(local); + } + } } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeLookupSwitch.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeLookupSwitch.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeStream.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeStream.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeSwitch.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeSwitch.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeTableSwitch.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BytecodeTableSwitch.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytecodes.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytecodes.java Wed Mar 07 09:50:36 2012 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,976 +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.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 && !field.isSynthetic()) { - assert field.getType() == int.class : "Field is not int : " + 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()); - } - } - - /** - * An 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]; - - /** - * An 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]; - - /** - * An array that maps from a bytecode value to the length in bytes for the corresponding instruction. - */ - private static final int[] lengthArray = new int[256]; - - /** - * An array that maps from a bytecode value to the estimated complexity of the bytecode in terms of generated machine code. - */ - private static final int[] compilationComplexityArray = new int[256]; - - // Checkstyle: stop - static { - def(NOP , "nop" , "b" , 0); - def(ACONST_NULL , "aconst_null" , "b" , 0); - def(ICONST_M1 , "iconst_m1" , "b" , 0); - def(ICONST_0 , "iconst_0" , "b" , 0); - def(ICONST_1 , "iconst_1" , "b" , 0); - def(ICONST_2 , "iconst_2" , "b" , 0); - def(ICONST_3 , "iconst_3" , "b" , 0); - def(ICONST_4 , "iconst_4" , "b" , 0); - def(ICONST_5 , "iconst_5" , "b" , 0); - def(LCONST_0 , "lconst_0" , "b" , 0); - def(LCONST_1 , "lconst_1" , "b" , 0); - def(FCONST_0 , "fconst_0" , "b" , 0); - def(FCONST_1 , "fconst_1" , "b" , 0); - def(FCONST_2 , "fconst_2" , "b" , 0); - def(DCONST_0 , "dconst_0" , "b" , 0); - def(DCONST_1 , "dconst_1" , "b" , 0); - def(BIPUSH , "bipush" , "bc" , 0); - def(SIPUSH , "sipush" , "bcc" , 0); - def(LDC , "ldc" , "bi" , 0, TRAP); - def(LDC_W , "ldc_w" , "bii" , 0, TRAP); - def(LDC2_W , "ldc2_w" , "bii" , 0, TRAP); - def(ILOAD , "iload" , "bi" , 0, LOAD); - def(LLOAD , "lload" , "bi" , 0, LOAD); - def(FLOAD , "fload" , "bi" , 0, LOAD); - def(DLOAD , "dload" , "bi" , 0, LOAD); - def(ALOAD , "aload" , "bi" , 0, LOAD); - def(ILOAD_0 , "iload_0" , "b" , 0, LOAD); - def(ILOAD_1 , "iload_1" , "b" , 0, LOAD); - def(ILOAD_2 , "iload_2" , "b" , 0, LOAD); - def(ILOAD_3 , "iload_3" , "b" , 0, LOAD); - def(LLOAD_0 , "lload_0" , "b" , 0, LOAD); - def(LLOAD_1 , "lload_1" , "b" , 0, LOAD); - def(LLOAD_2 , "lload_2" , "b" , 0, LOAD); - def(LLOAD_3 , "lload_3" , "b" , 0, LOAD); - def(FLOAD_0 , "fload_0" , "b" , 0, LOAD); - def(FLOAD_1 , "fload_1" , "b" , 0, LOAD); - def(FLOAD_2 , "fload_2" , "b" , 0, LOAD); - def(FLOAD_3 , "fload_3" , "b" , 0, LOAD); - def(DLOAD_0 , "dload_0" , "b" , 0, LOAD); - def(DLOAD_1 , "dload_1" , "b" , 0, LOAD); - def(DLOAD_2 , "dload_2" , "b" , 0, LOAD); - def(DLOAD_3 , "dload_3" , "b" , 0, LOAD); - def(ALOAD_0 , "aload_0" , "b" , 0, LOAD); - def(ALOAD_1 , "aload_1" , "b" , 0, LOAD); - def(ALOAD_2 , "aload_2" , "b" , 0, LOAD); - def(ALOAD_3 , "aload_3" , "b" , 0, LOAD); - def(IALOAD , "iaload" , "b" , 0, TRAP); - def(LALOAD , "laload" , "b" , 0, TRAP); - def(FALOAD , "faload" , "b" , 0, TRAP); - def(DALOAD , "daload" , "b" , 0, TRAP); - def(AALOAD , "aaload" , "b" , 0, TRAP); - def(BALOAD , "baload" , "b" , 0, TRAP); - def(CALOAD , "caload" , "b" , 0, TRAP); - def(SALOAD , "saload" , "b" , 0, TRAP); - def(ISTORE , "istore" , "bi" , 0, STORE); - def(LSTORE , "lstore" , "bi" , 0, STORE); - def(FSTORE , "fstore" , "bi" , 0, STORE); - def(DSTORE , "dstore" , "bi" , 0, STORE); - def(ASTORE , "astore" , "bi" , 0, STORE); - def(ISTORE_0 , "istore_0" , "b" , 0, STORE); - def(ISTORE_1 , "istore_1" , "b" , 0, STORE); - def(ISTORE_2 , "istore_2" , "b" , 0, STORE); - def(ISTORE_3 , "istore_3" , "b" , 0, STORE); - def(LSTORE_0 , "lstore_0" , "b" , 0, STORE); - def(LSTORE_1 , "lstore_1" , "b" , 0, STORE); - def(LSTORE_2 , "lstore_2" , "b" , 0, STORE); - def(LSTORE_3 , "lstore_3" , "b" , 0, STORE); - def(FSTORE_0 , "fstore_0" , "b" , 0, STORE); - def(FSTORE_1 , "fstore_1" , "b" , 0, STORE); - def(FSTORE_2 , "fstore_2" , "b" , 0, STORE); - def(FSTORE_3 , "fstore_3" , "b" , 0, STORE); - def(DSTORE_0 , "dstore_0" , "b" , 0, STORE); - def(DSTORE_1 , "dstore_1" , "b" , 0, STORE); - def(DSTORE_2 , "dstore_2" , "b" , 0, STORE); - def(DSTORE_3 , "dstore_3" , "b" , 0, STORE); - def(ASTORE_0 , "astore_0" , "b" , 0, STORE); - def(ASTORE_1 , "astore_1" , "b" , 0, STORE); - def(ASTORE_2 , "astore_2" , "b" , 0, STORE); - def(ASTORE_3 , "astore_3" , "b" , 0, STORE); - def(IASTORE , "iastore" , "b" , 3, TRAP); - def(LASTORE , "lastore" , "b" , 3, TRAP); - def(FASTORE , "fastore" , "b" , 3, TRAP); - def(DASTORE , "dastore" , "b" , 3, TRAP); - def(AASTORE , "aastore" , "b" , 4, TRAP); - def(BASTORE , "bastore" , "b" , 3, TRAP); - def(CASTORE , "castore" , "b" , 3, TRAP); - def(SASTORE , "sastore" , "b" , 3, TRAP); - def(POP , "pop" , "b" , 0); - def(POP2 , "pop2" , "b" , 0); - def(DUP , "dup" , "b" , 0); - def(DUP_X1 , "dup_x1" , "b" , 0); - def(DUP_X2 , "dup_x2" , "b" , 0); - def(DUP2 , "dup2" , "b" , 0); - def(DUP2_X1 , "dup2_x1" , "b" , 0); - def(DUP2_X2 , "dup2_x2" , "b" , 0); - def(SWAP , "swap" , "b" , 0); - def(IADD , "iadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(LADD , "ladd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(FADD , "fadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(DADD , "dadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(ISUB , "isub" , "b" , 1); - def(LSUB , "lsub" , "b" , 1); - def(FSUB , "fsub" , "b" , 1); - def(DSUB , "dsub" , "b" , 1); - def(IMUL , "imul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(LMUL , "lmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(FMUL , "fmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(DMUL , "dmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(IDIV , "idiv" , "b" , 1, TRAP); - def(LDIV , "ldiv" , "b" , 1, TRAP); - def(FDIV , "fdiv" , "b" , 1); - def(DDIV , "ddiv" , "b" , 1); - def(IREM , "irem" , "b" , 1, TRAP); - def(LREM , "lrem" , "b" , 1, TRAP); - def(FREM , "frem" , "b" , 1); - def(DREM , "drem" , "b" , 1); - def(INEG , "ineg" , "b" , 1); - def(LNEG , "lneg" , "b" , 1); - def(FNEG , "fneg" , "b" , 1); - def(DNEG , "dneg" , "b" , 1); - def(ISHL , "ishl" , "b" , 1); - def(LSHL , "lshl" , "b" , 1); - def(ISHR , "ishr" , "b" , 1); - def(LSHR , "lshr" , "b" , 1); - def(IUSHR , "iushr" , "b" , 1); - def(LUSHR , "lushr" , "b" , 1); - def(IAND , "iand" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(LAND , "land" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(IOR , "ior" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(LOR , "lor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(IXOR , "ixor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(LXOR , "lxor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); - def(IINC , "iinc" , "bic" , 1, LOAD | STORE); - def(I2L , "i2l" , "b" , 1); - def(I2F , "i2f" , "b" , 1); - def(I2D , "i2d" , "b" , 1); - def(L2I , "l2i" , "b" , 1); - def(L2F , "l2f" , "b" , 1); - def(L2D , "l2d" , "b" , 1); - def(F2I , "f2i" , "b" , 1); - def(F2L , "f2l" , "b" , 1); - def(F2D , "f2d" , "b" , 1); - def(D2I , "d2i" , "b" , 1); - def(D2L , "d2l" , "b" , 1); - def(D2F , "d2f" , "b" , 1); - def(I2B , "i2b" , "b" , 1); - def(I2C , "i2c" , "b" , 1); - def(I2S , "i2s" , "b" , 1); - def(LCMP , "lcmp" , "b" , 1); - def(FCMPL , "fcmpl" , "b" , 1); - def(FCMPG , "fcmpg" , "b" , 1); - def(DCMPL , "dcmpl" , "b" , 1); - def(DCMPG , "dcmpg" , "b" , 1); - def(IFEQ , "ifeq" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFNE , "ifne" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFLT , "iflt" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFGE , "ifge" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFGT , "ifgt" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFLE , "ifle" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IF_ICMPEQ , "if_icmpeq" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ICMPNE , "if_icmpne" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ICMPLT , "if_icmplt" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IF_ICMPGE , "if_icmpge" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IF_ICMPGT , "if_icmpgt" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IF_ICMPLE , "if_icmple" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IF_ACMPEQ , "if_acmpeq" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); - def(IF_ACMPNE , "if_acmpne" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); - def(GOTO , "goto" , "boo" , 1, STOP | BRANCH); - def(JSR , "jsr" , "boo" , 0, STOP | BRANCH); - def(RET , "ret" , "bi" , 0, STOP); - def(TABLESWITCH , "tableswitch" , "" , 4, STOP); - def(LOOKUPSWITCH , "lookupswitch" , "" , 4, STOP); - def(IRETURN , "ireturn" , "b" , 1, TRAP | STOP); - def(LRETURN , "lreturn" , "b" , 1, TRAP | STOP); - def(FRETURN , "freturn" , "b" , 1, TRAP | STOP); - def(DRETURN , "dreturn" , "b" , 1, TRAP | STOP); - def(ARETURN , "areturn" , "b" , 1, TRAP | STOP); - def(RETURN , "return" , "b" , 1, TRAP | STOP); - def(GETSTATIC , "getstatic" , "bjj" , 2, TRAP | FIELD_READ); - def(PUTSTATIC , "putstatic" , "bjj" , 2, TRAP | FIELD_WRITE); - def(GETFIELD , "getfield" , "bjj" , 2, TRAP | FIELD_READ); - def(PUTFIELD , "putfield" , "bjj" , 2, TRAP | FIELD_WRITE); - def(INVOKEVIRTUAL , "invokevirtual" , "bjj" , 7, TRAP | INVOKE); - def(INVOKESPECIAL , "invokespecial" , "bjj" , 5, TRAP | INVOKE); - def(INVOKESTATIC , "invokestatic" , "bjj" , 5, TRAP | INVOKE); - def(INVOKEINTERFACE , "invokeinterface" , "bjja_", 7, TRAP | INVOKE); - def(XXXUNUSEDXXX , "xxxunusedxxx" , "" , 0); - def(NEW , "new" , "bii" , 6, TRAP); - def(NEWARRAY , "newarray" , "bc" , 6, TRAP); - def(ANEWARRAY , "anewarray" , "bii" , 6, TRAP); - def(ARRAYLENGTH , "arraylength" , "b" , 2, TRAP); - def(ATHROW , "athrow" , "b" , 5, TRAP | STOP); - def(CHECKCAST , "checkcast" , "bii" , 3, TRAP); - def(INSTANCEOF , "instanceof" , "bii" , 4, TRAP); - def(MONITORENTER , "monitorenter" , "b" , 5, TRAP); - def(MONITOREXIT , "monitorexit" , "b" , 5, TRAP); - def(WIDE , "wide" , "" , 0); - def(MULTIANEWARRAY , "multianewarray" , "biic" , 6, TRAP); - def(IFNULL , "ifnull" , "boo" , 2, FALL_THROUGH | BRANCH); - def(IFNONNULL , "ifnonnull" , "boo" , 2, FALL_THROUGH | BRANCH); - def(GOTO_W , "goto_w" , "boooo", 1, STOP | BRANCH); - def(JSR_W , "jsr_w" , "boooo", 0, STOP | BRANCH); - def(BREAKPOINT , "breakpoint" , "b" , 0, 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 compilation complexity for a given opcode. - * @param opcode an opcode - * @return a value >= 0 - */ - public static int compilationComplexity(int opcode) { - return compilationComplexityArray[opcode & 0xff]; - } - - /** - * 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, int compilationComplexity) { - def(opcode, name, format, compilationComplexity, 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 compilationComplexity, 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; - compilationComplexityArray[opcode] = compilationComplexity; - 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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytes.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/Bytes.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java Wed Mar 07 10:02:33 2012 -0800 @@ -29,32 +29,29 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.graph.Node.Verbosity; +import com.oracle.max.graal.graph.iterators.*; 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 { - +public class FrameStateBuilder { + private final RiResolvedMethod method; private final StructuredGraph graph; private final ValueNode[] locals; private final ValueNode[] stack; - - private int stackIndex; + private int stackSize; private boolean rethrowException; - private final RiResolvedMethod method; - - public FrameStateBuilder(RiResolvedMethod method, int maxLocals, int maxStackSize, StructuredGraph graph, boolean eagerResolve) { + public FrameStateBuilder(RiResolvedMethod method, StructuredGraph graph, boolean eagerResolve) { assert graph != null; this.method = method; this.graph = graph; - this.locals = new ValueNode[maxLocals]; + this.locals = new ValueNode[method.maxLocals()]; // we always need at least one stack slot (for exceptions) - int stackSize = Math.max(1, maxStackSize); - this.stack = new ValueNode[stackSize]; + this.stack = new ValueNode[Math.max(1, method.maxStackSize())]; int javaIndex = 0; int index = 0; @@ -88,33 +85,259 @@ } } - @Override - public String toString() { - return String.format("FrameStateBuilder[stackSize=%d]", stackIndex); + private FrameStateBuilder(RiResolvedMethod method, StructuredGraph graph, ValueNode[] locals, ValueNode[] stack, int stackSize, boolean rethrowException) { + assert locals.length == method.maxLocals(); + assert stack.length == Math.max(1, method.maxStackSize()); + + this.method = method; + this.graph = graph; + this.locals = locals; + this.stack = stack; + this.stackSize = stackSize; + this.rethrowException = rethrowException; } - 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); + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[locals: ["); + for (int i = 0; i < locals.length; i++) { + sb.append(i == 0 ? "" : ",").append(locals[i] == null ? "_" : locals[i].toString(Verbosity.Id)); } - for (int i = 0; i < other.stackSize(); i++) { - stack[i] = other.stackAt(i); + sb.append("] stack: ["); + for (int i = 0; i < stackSize; i++) { + sb.append(i == 0 ? "" : ",").append(stack[i] == null ? "_" : stack[i].toString(Verbosity.Id)); } - this.rethrowException = other.rethrowException(); + sb.append("]"); + if (rethrowException) { + sb.append(" rethrowException"); + } + sb.append("]"); + return sb.toString(); } public FrameState create(int bci) { - return graph.add(new FrameState(method, bci, locals, stack, stackIndex, rethrowException, false)); + return graph.add(new FrameState(method, bci, locals, stack, stackSize, rethrowException, false)); + } + + public FrameState duplicateWithoutStack(int bci) { + return graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, false, false)); + } + + + public FrameStateBuilder copy() { + return new FrameStateBuilder(method, graph, Arrays.copyOf(locals, locals.length), Arrays.copyOf(stack, stack.length), stackSize, rethrowException); + } + + public FrameStateBuilder copyWithException(ValueNode exceptionObject) { + ValueNode[] newStack = new ValueNode[stack.length]; + newStack[0] = exceptionObject; + return new FrameStateBuilder(method, graph, Arrays.copyOf(locals, locals.length), newStack, 1, true); + } + + + public boolean isCompatibleWith(FrameStateBuilder other) { + assert method == other.method && graph == other.graph && localsSize() == other.localsSize() : "Can only compare frame states of the same method"; + + if (stackSize() != other.stackSize()) { + return false; + } + for (int i = 0; i < stackSize(); i++) { + ValueNode x = stackAt(i); + ValueNode y = other.stackAt(i); + if (x != y && ValueUtil.typeMismatch(x, y)) { + return false; + } + } + return true; + } + + public void merge(MergeNode block, FrameStateBuilder other) { + assert isCompatibleWith(other); + + for (int i = 0; i < localsSize(); i++) { + storeLocal(i, merge(localAt(i), other.localAt(i), block)); + } + for (int i = 0; i < stackSize(); i++) { + storeStack(i, merge(stackAt(i), other.stackAt(i), block)); + } + } + + private ValueNode merge(ValueNode currentValue, ValueNode otherValue, MergeNode block) { + if (currentValue == null) { + return null; + + } else if (block.isPhiAtMerge(currentValue)) { + if (otherValue == null || currentValue.kind() != otherValue.kind()) { + deletePhi(currentValue); + return null; + } + ((PhiNode) currentValue).addInput(otherValue); + return currentValue; + + } else if (currentValue != otherValue) { + assert !(block instanceof LoopBeginNode) : "Phi functions for loop headers are create eagerly for all locals and stack slots"; + if (otherValue == null || currentValue.kind() != otherValue.kind()) { + return null; + } + + PhiNode phi = graph.unique(new PhiNode(currentValue.kind(), block, PhiType.Value)); + for (int i = 0; i < block.phiPredecessorCount(); i++) { + phi.addInput(currentValue); + } + phi.addInput(otherValue); + assert phi.valueCount() == block.phiPredecessorCount() + 1 : "valueCount=" + phi.valueCount() + " predSize= " + block.phiPredecessorCount(); + return phi; + + } else { + return currentValue; + } + } + + private void deletePhi(Node phi) { + if (phi.isDeleted()) { + return; + } + // Collect all phi functions that use this phi so that we can delete them recursively (after we delete ourselfs to avoid circles). + List phiUsages = phi.usages().filter(NodePredicates.isA(PhiNode.class)).snapshot(); + + // Remove the phi function from all FrameStates where it is used and then delete it. + assert phi.usages().filter(NodePredicates.isNotA(FrameState.class)).filter(NodePredicates.isNotA(PhiNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states"; + phi.replaceAtUsages(null); + phi.safeDelete(); + + for (Node phiUsage : phiUsages) { + deletePhi(phiUsage); + } + } + + public void insertLoopPhis(LoopBeginNode loopBegin) { + for (int i = 0; i < localsSize(); i++) { + storeLocal(i, createLoopPhi(loopBegin, localAt(i))); + } + for (int i = 0; i < stackSize(); i++) { + storeStack(i, createLoopPhi(loopBegin, stackAt(i))); + } } - public FrameState duplicateWithException(int bci, ValueNode exceptionObject) { - FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, true, false)); - frameState.setOuterFrameState(outerFrameState()); - return frameState; + private PhiNode createLoopPhi(MergeNode block, ValueNode value) { + if (value == null) { + return null; + } + assert !block.isPhiAtMerge(value) : "phi function for this block already created"; + + PhiNode phi = graph.unique(new PhiNode(value.kind(), block, PhiType.Value)); + phi.addInput(value); + return phi; + } + + public void cleanupDeletedPhis() { + for (int i = 0; i < localsSize(); i++) { + if (localAt(i) != null && localAt(i).isDeleted()) { + assert localAt(i) instanceof PhiNode : "Only phi functions can be deleted during parsing"; + storeLocal(i, null); + } + } + } + + public void clearNonLiveLocals(BitMap liveness) { + if (liveness == null) { + return; + } + assert liveness.size() == locals.length; + for (int i = 0; i < locals.length; i++) { + if (!liveness.get(i)) { + locals[i] = null; + } + } + } + + public boolean rethrowException() { + return rethrowException; + } + + public void setRethrowException(boolean b) { + rethrowException = b; + } + + + /** + * Returns the size of the local variables. + * + * @return the size of the local variables + */ + public int localsSize() { + return locals.length; + } + + /** + * Gets the current size (height) of the stack. + */ + public int stackSize() { + return stackSize; + } + + /** + * Gets the value in the local variables at the specified index, without any sanity checking. + * + * @param i the index into the locals + * @return the instruction that produced the value for the specified local + */ + protected final ValueNode localAt(int i) { + return locals[i]; + } + + /** + * 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 + */ + protected final ValueNode stackAt(int i) { + return stack[i]; + } + + /** + * Loads the local variable at the specified index, checking that the returned value is non-null + * and that two-stack values are properly handled. + * + * @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]; + assert !x.isDeleted(); + assert !isTwoSlot(x.kind()) || locals[i + 1] == null; + assert i == 0 || locals[i - 1] == null || !isTwoSlot(locals[i - 1].kind()); + 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 (x != null && isTwoSlot(x.kind())) { + // if this is a double word, then kill i+1 + locals[i + 1] = null; + } + if (x != null && i > 0) { + ValueNode p = locals[i - 1]; + if (p != null && isTwoSlot(p.kind())) { + // if there was a double word at i - 1, then kill it + locals[i - 1] = null; + } + } + } + + private void storeStack(int i, ValueNode x) { + assert x == null || stack[i] == null || x.kind() == stack[i].kind() : "Method does not handle changes from one-slot to two-slot values"; + stack[i] = x; } /** @@ -123,7 +346,7 @@ * @param x the instruction to push onto the stack */ public void push(CiKind kind, ValueNode x) { - assert kind != CiKind.Void; + assert !x.isDeleted() && x.kind() != CiKind.Void && x.kind() != CiKind.Illegal; xpush(assertKind(kind, x)); if (isTwoSlot(kind)) { xpush(null); @@ -135,9 +358,8 @@ * @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; + assert x == null || (!x.isDeleted() && x.kind() != CiKind.Void && x.kind() != CiKind.Illegal); + stack[stackSize++] = x; } /** @@ -215,7 +437,7 @@ * @return x the instruction popped off the stack */ public ValueNode xpop() { - ValueNode result = stack[--stackIndex]; + ValueNode result = stack[--stackSize]; assert result == null || !result.isDeleted(); return result; } @@ -276,7 +498,7 @@ * @return an array containing the arguments off of the stack */ public ValueNode[] popArguments(int slotSize, int argSize) { - int base = stackIndex - slotSize; + int base = stackSize - slotSize; ValueNode[] r = new ValueNode[argSize]; int argIndex = 0; int stackindex = 0; @@ -286,7 +508,7 @@ r[argIndex++] = element; stackindex += stackSlots(element.kind()); } - stackIndex = base; + stackSize = base; return r; } @@ -309,173 +531,10 @@ } /** - * 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.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; - } - } - } - - /** - * 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]; - } - - /** - * Returns the size of the local variables. - * - * @return the size of the local variables - */ - public int localsSize() { - return locals.length; - } - - /** - * 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); - } - - - 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 { - return stack[i - locals.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, false, false)); - frameState.setOuterFrameState(outerFrameState()); - return frameState; - } - - @Override - public boolean rethrowException() { - return rethrowException; - } - - public void setRethrowException(boolean b) { - rethrowException = b; + stackSize = 0; } public static int stackSlots(CiKind kind) { diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java Wed Mar 07 10:02:33 2012 -0800 @@ -22,12 +22,13 @@ */ package com.oracle.max.graal.java; -import static com.oracle.max.graal.java.Bytecodes.*; +import static com.oracle.max.graal.java.bytecode.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.Representation; @@ -38,8 +39,8 @@ import com.oracle.max.graal.debug.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.java.BciBlockMapping.Block; -import com.oracle.max.graal.java.BciBlockMapping.DeoptBlock; import com.oracle.max.graal.java.BciBlockMapping.ExceptionBlock; +import com.oracle.max.graal.java.bytecode.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.DeoptimizeNode.DeoptAction; import com.oracle.max.graal.nodes.PhiNode.PhiType; @@ -47,7 +48,7 @@ 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.*; +import com.oracle.max.graal.nodes.type.*; /** * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph. @@ -80,30 +81,32 @@ 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; + + /** + * Node that marks the begin of block during bytecode parsing. When a block is identified the first + * time as a jump target, the placeholder is created and used as the successor for the jump. When the + * block is seen the second time, a MergeNode is created to correctly merge the now two different + * predecessor states. + */ + private static class BlockPlaceholderNode extends FixedWithNextNode implements Node.IterableNodeType { + public BlockPlaceholderNode() { + super(StampFactory.illegal()); + } + } + + public GraphBuilderPhase(RiRuntime runtime) { this(runtime, GraphBuilderConfiguration.getDefault()); } @@ -121,14 +124,12 @@ 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; this.currentGraph = graph; - this.frameState = new FrameStateBuilder(method, method.maxLocals(), method.maxStackSize(), graph, config.eagerResolving()); + this.frameState = new FrameStateBuilder(method, graph, config.eagerResolving()); build(); } @@ -140,12 +141,8 @@ private BciBlockMapping createBlockMap() { BciBlockMapping map = new BciBlockMapping(method, config.useBranchPrediction()); map.build(); + Debug.dump(map, CiUtil.format("After block building %f %R %H.%n(%P)", method)); -// if (currentContext.isObserved()) { -// String label = CiUtil.format("BlockListBuilder %f %R %H.%n(%P)", method); -// currentContext.observable.fireCompilationEvent(label, map); -// } - // TODO(tw): Reinstall this logging code when debug framework is finished. return map; } @@ -165,8 +162,6 @@ exceptionHandlers = blockMap.exceptionHandlers(); - nextBlockNumber = blockMap.blocks.size(); - lastInstr = currentGraph.start(); if (isSynchronized(method.accessFlags())) { // add a monitor enter to the start block @@ -174,6 +169,7 @@ methodSynchronizedObject = synchronizedObject(frameState, method); lastInstr = genMonitorEnter(methodSynchronizedObject); } + frameState.clearNonLiveLocals(blockMap.startBlock.localsLiveIn); // finish the start block ((AbstractStateSplit) lastInstr).setStateAfter(frameState.create(0)); @@ -181,14 +177,21 @@ appendGoto(createTarget(blockMap.startBlock, frameState)); } else { blockMap.startBlock.firstInstruction = lastInstr; + blockMap.startBlock.entryState = frameState; } - addToWorkList(blockMap.startBlock); - iterateAllBlocks(); + for (Block block : blockMap.blocks) { + processBlock(block); + } + processBlock(returnBlock); + processBlock(unwindBlock); + + Debug.dump(currentGraph, "After bytecode parsing"); + connectLoopEndToBegin(); // remove Placeholders (except for loop exits) - for (PlaceholderNode n : currentGraph.getNodes(PlaceholderNode.class)) { + for (BlockPlaceholderNode n : currentGraph.getNodes(BlockPlaceholderNode.class)) { currentGraph.removeFixed(n); } @@ -204,18 +207,13 @@ } } - private int nextBlockNumber() { - 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); + unwindBlock.blockID = Integer.MAX_VALUE; } return unwindBlock; } @@ -225,80 +223,11 @@ returnBlock = new Block(); returnBlock.startBci = bci; returnBlock.endBci = bci; - returnBlock.blockID = nextBlockNumber(); - addToWorkList(returnBlock); + returnBlock.blockID = Integer.MAX_VALUE; } 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, StateSplit first) { - - int bci = target.startBci; - if (target instanceof ExceptionBlock) { - bci = ((ExceptionBlock) target).deoptBci; - } - - FrameState existingState = first.stateAfter(); - if (existingState == null) { - // There was no state : new target - // copy state because it is modified - first.setStateAfter(newState.duplicate(bci)); - return; - } 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) { - // there is no merge yet here - PlaceholderNode p = (PlaceholderNode) first; - if (p.predecessor() == null) { - //nothing seems to come here, yet there's a state? - Debug.log("Funky control flow going to @bci %d : we already have a state but no predecessor", bci); - p.setStateAfter(newState.duplicate(bci)); - return; - } else { - //create a merge - MergeNode merge = currentGraph.add(new MergeNode()); - FixedNode next = p.next(); - if (p.predecessor() != null) { - EndNode end = currentGraph.add(new EndNode()); - p.setNext(end); - merge.addForwardEnd(end); - } - merge.setNext(next); - FrameState mergeState = existingState.duplicate(bci); - merge.setStateAfter(mergeState); - mergeState.merge(merge, newState); - target.firstInstruction = merge; - } - } else { - existingState.merge((MergeNode) first, newState); - } - } - } - public BytecodeStream stream() { return stream; } @@ -375,9 +304,9 @@ } else { currentExceptionObject = exceptionObject; } - FrameState stateWithException = frameState.duplicateWithException(bci, currentExceptionObject); + FrameStateBuilder stateWithException = frameState.copyWithException(currentExceptionObject); if (newObj != null) { - newObj.setStateAfter(stateWithException); + newObj.setStateAfter(stateWithException.create(bci)); } FixedNode target = createTarget(dispatchBlock, stateWithException); if (exceptionObject == null) { @@ -600,7 +529,7 @@ private void genIncrement() { int index = stream().readLocalIndex(); int delta = stream().readIncrement(); - ValueNode x = frameState.localAt(index); + ValueNode x = frameState.loadLocal(index); ValueNode y = append(ConstantNode.forInt(delta, currentGraph)); frameState.storeLocal(index, append(currentGraph.unique(new IntegerAddNode(CiKind.Int, x, y)))); } @@ -616,23 +545,25 @@ private void ifNode(ValueNode x, Condition cond, ValueNode y) { assert !x.isDeleted() && !y.isDeleted(); - double takenProbability = profilingInfo.getBranchTakenProbability(bci()); - if (takenProbability < 0) { - assert takenProbability == -1 : "invalid probability"; + assert currentBlock.normalSuccessors == 2 : currentBlock.normalSuccessors; + Block trueBlock = currentBlock.successors.get(0); + Block falseBlock = currentBlock.successors.get(1); + if (trueBlock == falseBlock) { + appendGoto(createTarget(trueBlock, frameState)); + return; + } + + double probability = profilingInfo.getBranchTakenProbability(bci()); + if (probability < 0) { + assert probability == -1 : "invalid probability"; Debug.log("missing probability in %s at bci %d", method, bci()); takenProbability = 0.5; } CompareNode condition = currentGraph.unique(new CompareNode(x, cond, y)); - FixedNode trueSuccessor = createTarget(currentBlock.successors.get(0), frameState); - FixedNode falseSuccessor = createTarget(currentBlock.successors.get(1), frameState); - if (trueSuccessor == falseSuccessor) { - appendGoto(trueSuccessor); - } else { - append(currentGraph.add(new IfNode(condition, trueSuccessor, falseSuccessor, takenProbability))); - } - - assert currentBlock.normalSuccessors == 2 : currentBlock.normalSuccessors; + BeginNode trueSuccessor = createBlockTarget(probability, trueBlock, frameState); + BeginNode falseSuccessor = createBlockTarget(1 - probability, falseBlock, frameState); + append(currentGraph.add(new IfNode(condition, trueSuccessor, falseSuccessor, probability))); } private void genIfZero(Condition cond) { @@ -765,7 +696,7 @@ InstanceOfNode instanceOfNode = new InstanceOfNode(hub, (RiResolvedType) type, object, hints, Util.isFinalClass(resolvedType), false); frameState.ipush(append(MaterializeNode.create(currentGraph.unique(instanceOfNode), currentGraph))); } else { - PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); + BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode()); DeoptimizeNode deopt = currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateRecompile)); IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new NullCheckNode(object, true)), trueSucc, deopt, 1)); append(ifNode); @@ -869,8 +800,8 @@ } private ExceptionInfo emitNullCheck(ValueNode receiver) { - PlaceholderNode trueSucc = currentGraph.add(new PlaceholderNode()); - PlaceholderNode falseSucc = currentGraph.add(new PlaceholderNode()); + BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode()); + BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode()); IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new NullCheckNode(receiver, false)), trueSucc, falseSucc, 1)); append(ifNode); @@ -881,15 +812,15 @@ return new ExceptionInfo(falseSucc, exception); } else { RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateNullPointerException)); - call.setStateAfter(frameState.duplicate(bci())); + call.setStateAfter(frameState.create(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()); + BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode()); + BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode()); IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new CompareNode(index, Condition.BT, length)), trueSucc, falseSucc, 1)); append(ifNode); @@ -900,7 +831,7 @@ return new ExceptionInfo(falseSucc, exception); } else { RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CiRuntimeCall.CreateOutOfBoundsException, new ValueNode[] {index})); - call.setStateAfter(frameState.duplicate(bci())); + call.setStateAfter(frameState.create(bci())); falseSucc.setNext(call); return new ExceptionInfo(call, call); } @@ -930,7 +861,7 @@ merge.addForwardEnd(end); phi.addInput(info.exception); } - merge.setStateAfter(frameState.duplicate(bci())); + merge.setStateAfter(frameState.create(bci())); exception = new ExceptionInfo(merge, phi); } @@ -938,7 +869,7 @@ if (entry != null) { exception.exceptionEdge.setNext(entry); } else { - exception.exceptionEdge.setNext(createTarget(unwindBlock(bci()), frameState.duplicateWithException(bci(), exception.exception))); + exception.exceptionEdge.setNext(createTarget(unwindBlock(bci()), frameState.copyWithException(exception.exception))); } Debug.metric("ExplicitExceptions").increment(); } @@ -1106,6 +1037,10 @@ result = append(invoke); frameState.pushReturn(resultType, result); Block nextBlock = currentBlock.successors.get(0); + + assert bci() == currentBlock.endBci; + frameState.clearNonLiveLocals(currentBlock.localsLiveOut); + invoke.setNext(createTarget(nextBlock, frameState)); invoke.setStateAfter(frameState.create(nextBlock.startBci)); } else { @@ -1193,9 +1128,10 @@ int nofCases = ts.numberOfCases() + 1; // including default case assert currentBlock.normalSuccessors == nofCases; - TableSwitchNode tableSwitch = currentGraph.add(new TableSwitchNode(value, ts.lowKey(), switchProbability(nofCases, bci))); + double[] probabilities = switchProbability(nofCases, bci); + TableSwitchNode tableSwitch = currentGraph.add(new TableSwitchNode(value, ts.lowKey(), probabilities)); for (int i = 0; i < nofCases; ++i) { - tableSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); + tableSwitch.setBlockSuccessor(i, createBlockTarget(probabilities[i], currentBlock.successors.get(i), frameState)); } append(tableSwitch); } @@ -1226,9 +1162,10 @@ for (int i = 0; i < nofCases - 1; ++i) { keys[i] = ls.keyAt(i); } - LookupSwitchNode lookupSwitch = currentGraph.add(new LookupSwitchNode(value, keys, switchProbability(nofCases, bci))); + double[] probabilities = switchProbability(nofCases, bci); + LookupSwitchNode lookupSwitch = currentGraph.add(new LookupSwitchNode(value, keys, probabilities)); for (int i = 0; i < nofCases; ++i) { - lookupSwitch.setBlockSuccessor(i, BeginNode.begin(createTarget(currentBlock.successors.get(i), frameState))); + lookupSwitch.setBlockSuccessor(i, createBlockTarget(probabilities[i], currentBlock.successors.get(i), frameState)); } append(lookupSwitch); } @@ -1259,134 +1196,170 @@ return x; } - private FixedNode createTarget(Block block, FrameStateAccess stateAfter) { + private FixedNode createTarget(Block block, FrameStateBuilder 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; - } + assert !block.isExceptionEntry || stateAfter.stackSize() == 1; if (block.firstInstruction == null) { - if (block.isLoopHeader) { - LoopBeginNode loop = currentGraph.add(new LoopBeginNode()); - EndNode end = currentGraph.add(new EndNode()); - loop.addForwardEnd(end); - PlaceholderNode p = currentGraph.add(new PlaceholderNode()); - p.setNext(end); - block.firstInstruction = p; - } else { - block.firstInstruction = currentGraph.add(new PlaceholderNode()); - } + // This is the first time we see this block as a branch target. + // Create and return a placeholder that later can be replaced with a MergeNode when we see this block again. + block.firstInstruction = currentGraph.add(new BlockPlaceholderNode()); + block.entryState = stateAfter.copy(); + block.entryState.clearNonLiveLocals(block.localsLiveIn); + + Debug.log("createTarget %s: first visit, result: %s", block, block.firstInstruction); + return block.firstInstruction; } - LoopEndNode loopend = null; - StateSplit mergeAt = null; - if (block.isLoopHeader && isVisited(block)) { // backedge - loopend = currentGraph.add(new LoopEndNode(loopBegin(block))); - mergeAt = loopBegin(block); - } else { - mergeAt = (StateSplit) block.firstInstruction; + + // We already saw this block before, so we have to merge states. + if (!block.entryState.isCompatibleWith(stateAfter)) { + throw new CiBailout("stacks do not match; bytecodes would not verify"); } - mergeOrClone(block, stateAfter, mergeAt); - addToWorkList(block); + if (block.firstInstruction instanceof LoopBeginNode) { + assert block.isLoopHeader && currentBlock.blockID >= block.blockID : "must be backward branch"; + // Backward loop edge. We need to create a special LoopEndNode and merge with the loop begin node created before. + LoopBeginNode loopBegin = (LoopBeginNode) block.firstInstruction; + LoopEndNode result = currentGraph.add(new LoopEndNode(loopBegin)); + block.entryState.merge(loopBegin, stateAfter); + + Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result); + return result; + } + assert currentBlock == null || currentBlock.blockID < block.blockID : "must not be backward branch"; + assert block.firstInstruction.next() == null : "bytecodes already parsed for block"; - FixedNode result = null; - if (loopend != null) { - result = loopend; - } else { - result = block.firstInstruction; + if (block.firstInstruction instanceof BlockPlaceholderNode) { + // This is the second time we see this block. Create the actual MergeNode and the End Node for the already existing edge. + // For simplicity, we leave the placeholder in the graph and just append the new nodes after the placeholder. + BlockPlaceholderNode placeholder = (BlockPlaceholderNode) block.firstInstruction; + + // The EndNode for the already existing edge. + EndNode end = currentGraph.add(new EndNode()); + // The MergeNode that replaces the placeholder. + MergeNode mergeNode = currentGraph.add(new MergeNode()); + FixedNode next = placeholder.next(); + + placeholder.setNext(end); + mergeNode.addForwardEnd(end); + mergeNode.setNext(next); + + block.firstInstruction = mergeNode; } - if (result instanceof MergeNode) { - EndNode end = currentGraph.add(new EndNode()); - ((MergeNode) result).addForwardEnd(end); - result = end; - } - assert !(result instanceof MergeNode); - Debug.log("createTarget(%s, state) = %s", block, result); + MergeNode mergeNode = (MergeNode) block.firstInstruction; + + // The EndNode for the newly merged edge. + EndNode result = currentGraph.add(new EndNode()); + block.entryState.merge(mergeNode, stateAfter); + mergeNode.addForwardEnd(result); + + Debug.log("createTarget %s: merging state, result: %s", block, result); return result; } - private ValueNode synchronizedObject(FrameStateAccess state, RiResolvedMethod target) { + /** + * Returns a block begin node with the specified state. If the specified probability is 0, the block + * deoptimizes immediately. + */ + private BeginNode createBlockTarget(double probability, Block block, FrameStateBuilder stateAfter) { + assert probability >= 0 && probability <= 1; + if (probability == 0) { + FrameStateBuilder state = stateAfter.copy(); + state.clearNonLiveLocals(block.localsLiveIn); + + BeginNode begin = currentGraph.add(new BeginNode()); + DeoptimizeNode deopt = currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile)); + begin.setNext(deopt); + begin.setStateAfter(state.create(block.startBci)); + return begin; + } + + FixedNode target = createTarget(block, stateAfter); + assert !(target instanceof BeginNode); + BeginNode begin = currentGraph.add(new BeginNode()); + begin.setNext(target); + return begin; + } + + private ValueNode synchronizedObject(FrameStateBuilder state, RiResolvedMethod target) { if (isStatic(target.accessFlags())) { return append(ConstantNode.forCiConstant(target.holder().getEncoding(Representation.JavaClass), runtime, currentGraph)); } else { - return state.localAt(0); + return state.loadLocal(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; - } + private void processBlock(Block block) { + // Ignore blocks that have no predecessors by the time it their bytecodes are parsed + if (block == null || block.firstInstruction == null) { + Debug.log("Ignoring block %s", block); + return; + } + Debug.log("Parsing block %s firstInstruction: %s loopHeader: %b", block, block.firstInstruction, block.isLoopHeader); + + lastInstr = block.firstInstruction; + frameState = block.entryState; + currentBlock = block; - if (!isVisited(block)) { - markVisited(block); - // now parse the block - if (block.isLoopHeader) { - LoopBeginNode begin = loopBegin(block); - FrameState preLoopState = ((StateSplit) block.firstInstruction).stateAfter(); - FrameState loopState = preLoopState.duplicate(preLoopState.bci); - begin.setStateAfter(loopState); - loopState.insertLoopPhis(begin); - lastInstr = begin; - } else { - lastInstr = block.firstInstruction; - } - frameState.initializeFrom(((StateSplit) lastInstr).stateAfter()); - assert lastInstr.next() == null : "instructions already appended at block " + block.blockID; + frameState.cleanupDeletedPhis(); + if (lastInstr instanceof MergeNode) { + int bci = block.startBci; + if (block instanceof ExceptionBlock) { + bci = ((ExceptionBlock) block).deoptBci; + } + ((MergeNode) lastInstr).setStateAfter(frameState.create(bci)); + } - if (block == returnBlock) { - frameState.setRethrowException(false); - createReturn(); - } else if (block == unwindBlock) { - frameState.setRethrowException(false); - createUnwind(); - } else if (block instanceof ExceptionBlock) { - createExceptionDispatch((ExceptionBlock) block); - } else if (block instanceof DeoptBlock) { - createDeopt(); - } else { - frameState.setRethrowException(false); - iterateBytecodesForBlock(block); - } - } + if (block == returnBlock) { + frameState.setRethrowException(false); + createReturn(); + } else if (block == unwindBlock) { + frameState.setRethrowException(false); + createUnwind(); + } else if (block instanceof ExceptionBlock) { + createExceptionDispatch((ExceptionBlock) block); + } else { + frameState.setRethrowException(false); + iterateBytecodesForBlock(block); } } private void connectLoopEndToBegin() { for (LoopBeginNode begin : currentGraph.getNodes(LoopBeginNode.class)) { if (begin.loopEnds().isEmpty()) { -// This can happen with degenerated loops like this one: -// for (;;) { -// try { -// break; -// } catch (UnresolvedException iioe) { -// } -// } - - // Remove the loop begin or transform it into a merge. - assert begin.forwardEndCount() > 0; + // Remove loop header without loop ends. + // This can happen with degenerated loops like this one: + // for (;;) { + // try { + // break; + // } catch (UnresolvedException iioe) { + // } + // } + assert begin.forwardEndCount() == 1; currentGraph.reduceDegenerateLoopBegin(begin); } else { - begin.stateAfter().simplifyLoopState(); + // Delete unnecessary loop phi functions, i.e., phi functions where all inputs are either the same or the phi itself. + for (PhiNode phi : begin.stateAfter().values().filter(PhiNode.class).snapshot()) { + checkRedundantPhi(phi); + } } } } - private static LoopBeginNode loopBegin(Block block) { - LoopBeginNode loopBegin = (LoopBeginNode) ((EndNode) block.firstInstruction.next()).merge(); - return loopBegin; - } + private static void checkRedundantPhi(PhiNode phiNode) { + if (phiNode.isDeleted() || phiNode.valueCount() == 1) { + return; + } - private void createDeopt() { - append(currentGraph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile))); + ValueNode singleValue = phiNode.singleValue(); + if (singleValue != null) { + Collection phiUsages = phiNode.usages().filter(PhiNode.class).snapshot(); + ((StructuredGraph) phiNode.graph()).replaceFloating(phiNode, singleValue); + for (PhiNode phi : phiUsages) { + checkRedundantPhi(phi); + } + } } private void createUnwind() { @@ -1480,9 +1453,30 @@ } private void iterateBytecodesForBlock(Block block) { - assert frameState != null; + if (block.isLoopHeader) { + // Create the loop header block, which later will merge the backward branches of the loop. + EndNode preLoopEnd = currentGraph.add(new EndNode()); + LoopBeginNode loopBegin = currentGraph.add(new LoopBeginNode()); + lastInstr.setNext(preLoopEnd); + // Add the single non-loop predecessor of the loop header. + loopBegin.addForwardEnd(preLoopEnd); + lastInstr = loopBegin; - currentBlock = block; + // Create phi functions for all local variables and operand stack slots. + frameState.insertLoopPhis(loopBegin); + loopBegin.setStateAfter(frameState.create(block.startBci)); + + // We have seen all forward branches. All subsequent backward branches will merge to the loop header. + // This ensures that the loop header has exactly one non-loop predecessor. + block.firstInstruction = loopBegin; + // We need to preserve the frame state builder of the loop header so that we can merge values for + // phi functions, so make a copy of it. + block.entryState = frameState.copy(); + + Debug.log(" created loop header %s", loopBegin); + } + assert lastInstr.next() == null : "instructions already appended at block " + block; + Debug.log(" frameState: %s", frameState); int endBCI = stream.endBCI(); @@ -1737,7 +1731,7 @@ 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 JSR_W : genJsr(stream.readBranchDest()); break; case BREAKPOINT: throw new CiBailout("concurrent setting of breakpoint"); default: @@ -1769,31 +1763,4 @@ 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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrScope.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrScope.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/JsrScope.java Wed Mar 07 10:02:33 2012 -0800 @@ -51,6 +51,10 @@ return scope == 0; } + public boolean isPrefixOf(JsrScope other) { + return (scope & other.scope) == scope; + } + public JsrScope pop() { return new JsrScope(scope >>> 16); } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/BytecodeLookupSwitch.java Wed Mar 07 10:02:33 2012 -0800 @@ -0,0 +1,67 @@ +/* + * 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.bytecode; + +/** + * A utility for processing {@link Bytecodes#LOOKUPSWITCH} bytecodes. + */ +public 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); + } + + @Override + public int defaultOffset() { + return stream.readInt(alignedBci); + } + + @Override + public int offsetAt(int i) { + return stream.readInt(alignedBci + OFFSET_TO_FIRST_PAIR_OFFSET + PAIR_SIZE * i); + } + + @Override + public int keyAt(int i) { + return stream.readInt(alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * i); + } + + @Override + public int numberOfCases() { + return stream.readInt(alignedBci + OFFSET_TO_NUMBER_PAIRS); + } + + @Override + public int size() { + return alignedBci + OFFSET_TO_FIRST_PAIR_MATCH + PAIR_SIZE * numberOfCases() - bci; + } +} diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/BytecodeStream.java Wed Mar 07 10:02:33 2012 -0800 @@ -0,0 +1,222 @@ +/* + * 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.bytecode; + +/** + * 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 + if (opcode == Bytecodes.GOTO_W || opcode == Bytecodes.JSR_W) { + return curBCI + Bytes.beS4(code, curBCI + 1); + } else { + return curBCI + Bytes.beS2(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 + lengthOf(); + } else { + opcode = Bytecodes.END; + nextBCI = curBCI; + } + } + + /** + * Gets the length of the current bytecode. + */ + private int lengthOf() { + int length = Bytecodes.lengthOf(opcode); + if (length == 0) { + switch (opcode) { + case Bytecodes.TABLESWITCH: { + return new BytecodeTableSwitch(this, curBCI).size(); + } + case Bytecodes.LOOKUPSWITCH: { + return new BytecodeLookupSwitch(this, curBCI).size(); + } + case Bytecodes.WIDE: { + int opc = Bytes.beU1(code, curBCI + 1); + if (opc == Bytecodes.RET) { + return 4; + } else if (opc == Bytecodes.IINC) { + return 6; + } else { + return 4; // a load or store bytecode + } + } + default: + throw new Error("unknown variable-length bytecode: " + opcode); + } + } + return length; + } +} diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/BytecodeSwitch.java Wed Mar 07 10:02:33 2012 -0800 @@ -0,0 +1,110 @@ +/* + * 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.bytecode; + +/** + * 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 the bytecode array. + */ + protected final BytecodeStream stream; + /** + * 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.stream = stream; + this.bci = bci; + this.alignedBci = (bci + 4) & 0xfffffffc; + } + + /** + * 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(); +} diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/BytecodeTableSwitch.java Wed Mar 07 10:02:33 2012 -0800 @@ -0,0 +1,83 @@ +/* + * 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.bytecode; + +/** + * 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); + } + + /** + * Gets the low key of the table switch. + * @return the low key + */ + public int lowKey() { + return stream.readInt(alignedBci + OFFSET_TO_LOW_KEY); + } + + /** + * Gets the high key of the table switch. + * @return the high key + */ + public int highKey() { + return stream.readInt(alignedBci + OFFSET_TO_HIGH_KEY); + } + + @Override + public int keyAt(int i) { + return lowKey() + i; + } + + @Override + public int defaultOffset() { + return stream.readInt(alignedBci); + } + + @Override + public int offsetAt(int i) { + return stream.readInt(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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/Bytecodes.java Wed Mar 07 10:02:33 2012 -0800 @@ -0,0 +1,940 @@ +/* + * 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.bytecode; + +import static com.oracle.max.graal.java.bytecode.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 && !field.isSynthetic()) { + assert field.getType() == int.class : "Field is not int : " + 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()); + } + } + + /** + * An 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]; + + /** + * An 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]; + + /** + * An array that maps from a bytecode value to the length in bytes for the corresponding instruction. + */ + private static final int[] lengthArray = new int[256]; + + /** + * An array that maps from a bytecode value to the estimated complexity of the bytecode in terms of generated machine code. + */ + private static final int[] compilationComplexityArray = new int[256]; + + // Checkstyle: stop + static { + def(NOP , "nop" , "b" , 0); + def(ACONST_NULL , "aconst_null" , "b" , 0); + def(ICONST_M1 , "iconst_m1" , "b" , 0); + def(ICONST_0 , "iconst_0" , "b" , 0); + def(ICONST_1 , "iconst_1" , "b" , 0); + def(ICONST_2 , "iconst_2" , "b" , 0); + def(ICONST_3 , "iconst_3" , "b" , 0); + def(ICONST_4 , "iconst_4" , "b" , 0); + def(ICONST_5 , "iconst_5" , "b" , 0); + def(LCONST_0 , "lconst_0" , "b" , 0); + def(LCONST_1 , "lconst_1" , "b" , 0); + def(FCONST_0 , "fconst_0" , "b" , 0); + def(FCONST_1 , "fconst_1" , "b" , 0); + def(FCONST_2 , "fconst_2" , "b" , 0); + def(DCONST_0 , "dconst_0" , "b" , 0); + def(DCONST_1 , "dconst_1" , "b" , 0); + def(BIPUSH , "bipush" , "bc" , 0); + def(SIPUSH , "sipush" , "bcc" , 0); + def(LDC , "ldc" , "bi" , 0, TRAP); + def(LDC_W , "ldc_w" , "bii" , 0, TRAP); + def(LDC2_W , "ldc2_w" , "bii" , 0, TRAP); + def(ILOAD , "iload" , "bi" , 0, LOAD); + def(LLOAD , "lload" , "bi" , 0, LOAD); + def(FLOAD , "fload" , "bi" , 0, LOAD); + def(DLOAD , "dload" , "bi" , 0, LOAD); + def(ALOAD , "aload" , "bi" , 0, LOAD); + def(ILOAD_0 , "iload_0" , "b" , 0, LOAD); + def(ILOAD_1 , "iload_1" , "b" , 0, LOAD); + def(ILOAD_2 , "iload_2" , "b" , 0, LOAD); + def(ILOAD_3 , "iload_3" , "b" , 0, LOAD); + def(LLOAD_0 , "lload_0" , "b" , 0, LOAD); + def(LLOAD_1 , "lload_1" , "b" , 0, LOAD); + def(LLOAD_2 , "lload_2" , "b" , 0, LOAD); + def(LLOAD_3 , "lload_3" , "b" , 0, LOAD); + def(FLOAD_0 , "fload_0" , "b" , 0, LOAD); + def(FLOAD_1 , "fload_1" , "b" , 0, LOAD); + def(FLOAD_2 , "fload_2" , "b" , 0, LOAD); + def(FLOAD_3 , "fload_3" , "b" , 0, LOAD); + def(DLOAD_0 , "dload_0" , "b" , 0, LOAD); + def(DLOAD_1 , "dload_1" , "b" , 0, LOAD); + def(DLOAD_2 , "dload_2" , "b" , 0, LOAD); + def(DLOAD_3 , "dload_3" , "b" , 0, LOAD); + def(ALOAD_0 , "aload_0" , "b" , 0, LOAD); + def(ALOAD_1 , "aload_1" , "b" , 0, LOAD); + def(ALOAD_2 , "aload_2" , "b" , 0, LOAD); + def(ALOAD_3 , "aload_3" , "b" , 0, LOAD); + def(IALOAD , "iaload" , "b" , 0, TRAP); + def(LALOAD , "laload" , "b" , 0, TRAP); + def(FALOAD , "faload" , "b" , 0, TRAP); + def(DALOAD , "daload" , "b" , 0, TRAP); + def(AALOAD , "aaload" , "b" , 0, TRAP); + def(BALOAD , "baload" , "b" , 0, TRAP); + def(CALOAD , "caload" , "b" , 0, TRAP); + def(SALOAD , "saload" , "b" , 0, TRAP); + def(ISTORE , "istore" , "bi" , 0, STORE); + def(LSTORE , "lstore" , "bi" , 0, STORE); + def(FSTORE , "fstore" , "bi" , 0, STORE); + def(DSTORE , "dstore" , "bi" , 0, STORE); + def(ASTORE , "astore" , "bi" , 0, STORE); + def(ISTORE_0 , "istore_0" , "b" , 0, STORE); + def(ISTORE_1 , "istore_1" , "b" , 0, STORE); + def(ISTORE_2 , "istore_2" , "b" , 0, STORE); + def(ISTORE_3 , "istore_3" , "b" , 0, STORE); + def(LSTORE_0 , "lstore_0" , "b" , 0, STORE); + def(LSTORE_1 , "lstore_1" , "b" , 0, STORE); + def(LSTORE_2 , "lstore_2" , "b" , 0, STORE); + def(LSTORE_3 , "lstore_3" , "b" , 0, STORE); + def(FSTORE_0 , "fstore_0" , "b" , 0, STORE); + def(FSTORE_1 , "fstore_1" , "b" , 0, STORE); + def(FSTORE_2 , "fstore_2" , "b" , 0, STORE); + def(FSTORE_3 , "fstore_3" , "b" , 0, STORE); + def(DSTORE_0 , "dstore_0" , "b" , 0, STORE); + def(DSTORE_1 , "dstore_1" , "b" , 0, STORE); + def(DSTORE_2 , "dstore_2" , "b" , 0, STORE); + def(DSTORE_3 , "dstore_3" , "b" , 0, STORE); + def(ASTORE_0 , "astore_0" , "b" , 0, STORE); + def(ASTORE_1 , "astore_1" , "b" , 0, STORE); + def(ASTORE_2 , "astore_2" , "b" , 0, STORE); + def(ASTORE_3 , "astore_3" , "b" , 0, STORE); + def(IASTORE , "iastore" , "b" , 3, TRAP); + def(LASTORE , "lastore" , "b" , 3, TRAP); + def(FASTORE , "fastore" , "b" , 3, TRAP); + def(DASTORE , "dastore" , "b" , 3, TRAP); + def(AASTORE , "aastore" , "b" , 4, TRAP); + def(BASTORE , "bastore" , "b" , 3, TRAP); + def(CASTORE , "castore" , "b" , 3, TRAP); + def(SASTORE , "sastore" , "b" , 3, TRAP); + def(POP , "pop" , "b" , 0); + def(POP2 , "pop2" , "b" , 0); + def(DUP , "dup" , "b" , 0); + def(DUP_X1 , "dup_x1" , "b" , 0); + def(DUP_X2 , "dup_x2" , "b" , 0); + def(DUP2 , "dup2" , "b" , 0); + def(DUP2_X1 , "dup2_x1" , "b" , 0); + def(DUP2_X2 , "dup2_x2" , "b" , 0); + def(SWAP , "swap" , "b" , 0); + def(IADD , "iadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(LADD , "ladd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(FADD , "fadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(DADD , "dadd" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(ISUB , "isub" , "b" , 1); + def(LSUB , "lsub" , "b" , 1); + def(FSUB , "fsub" , "b" , 1); + def(DSUB , "dsub" , "b" , 1); + def(IMUL , "imul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(LMUL , "lmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(FMUL , "fmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(DMUL , "dmul" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(IDIV , "idiv" , "b" , 1, TRAP); + def(LDIV , "ldiv" , "b" , 1, TRAP); + def(FDIV , "fdiv" , "b" , 1); + def(DDIV , "ddiv" , "b" , 1); + def(IREM , "irem" , "b" , 1, TRAP); + def(LREM , "lrem" , "b" , 1, TRAP); + def(FREM , "frem" , "b" , 1); + def(DREM , "drem" , "b" , 1); + def(INEG , "ineg" , "b" , 1); + def(LNEG , "lneg" , "b" , 1); + def(FNEG , "fneg" , "b" , 1); + def(DNEG , "dneg" , "b" , 1); + def(ISHL , "ishl" , "b" , 1); + def(LSHL , "lshl" , "b" , 1); + def(ISHR , "ishr" , "b" , 1); + def(LSHR , "lshr" , "b" , 1); + def(IUSHR , "iushr" , "b" , 1); + def(LUSHR , "lushr" , "b" , 1); + def(IAND , "iand" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(LAND , "land" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(IOR , "ior" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(LOR , "lor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(IXOR , "ixor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(LXOR , "lxor" , "b" , 1, COMMUTATIVE | ASSOCIATIVE); + def(IINC , "iinc" , "bic" , 1, LOAD | STORE); + def(I2L , "i2l" , "b" , 1); + def(I2F , "i2f" , "b" , 1); + def(I2D , "i2d" , "b" , 1); + def(L2I , "l2i" , "b" , 1); + def(L2F , "l2f" , "b" , 1); + def(L2D , "l2d" , "b" , 1); + def(F2I , "f2i" , "b" , 1); + def(F2L , "f2l" , "b" , 1); + def(F2D , "f2d" , "b" , 1); + def(D2I , "d2i" , "b" , 1); + def(D2L , "d2l" , "b" , 1); + def(D2F , "d2f" , "b" , 1); + def(I2B , "i2b" , "b" , 1); + def(I2C , "i2c" , "b" , 1); + def(I2S , "i2s" , "b" , 1); + def(LCMP , "lcmp" , "b" , 1); + def(FCMPL , "fcmpl" , "b" , 1); + def(FCMPG , "fcmpg" , "b" , 1); + def(DCMPL , "dcmpl" , "b" , 1); + def(DCMPG , "dcmpg" , "b" , 1); + def(IFEQ , "ifeq" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFNE , "ifne" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFLT , "iflt" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFGE , "ifge" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFGT , "ifgt" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFLE , "ifle" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IF_ICMPEQ , "if_icmpeq" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ICMPNE , "if_icmpne" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ICMPLT , "if_icmplt" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IF_ICMPGE , "if_icmpge" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IF_ICMPGT , "if_icmpgt" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IF_ICMPLE , "if_icmple" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IF_ACMPEQ , "if_acmpeq" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); + def(IF_ACMPNE , "if_acmpne" , "boo" , 2, COMMUTATIVE | FALL_THROUGH | BRANCH); + def(GOTO , "goto" , "boo" , 1, STOP | BRANCH); + def(JSR , "jsr" , "boo" , 0, STOP | BRANCH); + def(RET , "ret" , "bi" , 0, STOP); + def(TABLESWITCH , "tableswitch" , "" , 4, STOP); + def(LOOKUPSWITCH , "lookupswitch" , "" , 4, STOP); + def(IRETURN , "ireturn" , "b" , 1, TRAP | STOP); + def(LRETURN , "lreturn" , "b" , 1, TRAP | STOP); + def(FRETURN , "freturn" , "b" , 1, TRAP | STOP); + def(DRETURN , "dreturn" , "b" , 1, TRAP | STOP); + def(ARETURN , "areturn" , "b" , 1, TRAP | STOP); + def(RETURN , "return" , "b" , 1, TRAP | STOP); + def(GETSTATIC , "getstatic" , "bjj" , 2, TRAP | FIELD_READ); + def(PUTSTATIC , "putstatic" , "bjj" , 2, TRAP | FIELD_WRITE); + def(GETFIELD , "getfield" , "bjj" , 2, TRAP | FIELD_READ); + def(PUTFIELD , "putfield" , "bjj" , 2, TRAP | FIELD_WRITE); + def(INVOKEVIRTUAL , "invokevirtual" , "bjj" , 7, TRAP | INVOKE); + def(INVOKESPECIAL , "invokespecial" , "bjj" , 5, TRAP | INVOKE); + def(INVOKESTATIC , "invokestatic" , "bjj" , 5, TRAP | INVOKE); + def(INVOKEINTERFACE , "invokeinterface" , "bjja_", 7, TRAP | INVOKE); + def(XXXUNUSEDXXX , "xxxunusedxxx" , "" , 0); + def(NEW , "new" , "bii" , 6, TRAP); + def(NEWARRAY , "newarray" , "bc" , 6, TRAP); + def(ANEWARRAY , "anewarray" , "bii" , 6, TRAP); + def(ARRAYLENGTH , "arraylength" , "b" , 2, TRAP); + def(ATHROW , "athrow" , "b" , 5, TRAP | STOP); + def(CHECKCAST , "checkcast" , "bii" , 3, TRAP); + def(INSTANCEOF , "instanceof" , "bii" , 4, TRAP); + def(MONITORENTER , "monitorenter" , "b" , 5, TRAP); + def(MONITOREXIT , "monitorexit" , "b" , 5, TRAP); + def(WIDE , "wide" , "" , 0); + def(MULTIANEWARRAY , "multianewarray" , "biic" , 6, TRAP); + def(IFNULL , "ifnull" , "boo" , 2, FALL_THROUGH | BRANCH); + def(IFNONNULL , "ifnonnull" , "boo" , 2, FALL_THROUGH | BRANCH); + def(GOTO_W , "goto_w" , "boooo", 1, STOP | BRANCH); + def(JSR_W , "jsr_w" , "boooo", 0, STOP | BRANCH); + def(BREAKPOINT , "breakpoint" , "b" , 0, 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 compilation complexity for a given opcode. + * @param opcode an opcode + * @return a value >= 0 + */ + public static int compilationComplexity(int opcode) { + return compilationComplexityArray[opcode & 0xff]; + } + + /** + * 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, int compilationComplexity) { + def(opcode, name, format, compilationComplexity, 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 compilationComplexity, 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; + compilationComplexityArray[opcode] = compilationComplexity; + 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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/bytecode/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/bytecode/Bytes.java Wed Mar 07 10:02:33 2012 -0800 @@ -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.bytecode; + +/** + * 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 e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/package-info.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/package-info.java Wed Mar 07 09:50:36 2012 -0800 +++ /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.java; diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java Wed Mar 07 10:02:33 2012 -0800 @@ -28,7 +28,6 @@ import com.oracle.max.cri.ri.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.graph.iterators.*; -import com.oracle.max.graal.nodes.PhiNode.PhiType; import com.oracle.max.graal.nodes.spi.*; import com.oracle.max.graal.nodes.virtual.*; @@ -36,7 +35,7 @@ * The {@code FrameState} class encapsulates the frame state (i.e. local variables and * operand stack) at a particular point in the abstract interpretation. */ -public final class FrameState extends Node implements FrameStateAccess, Node.IterableNodeType, LIRLowerable { +public final class FrameState extends Node implements Node.IterableNodeType, LIRLowerable { protected final int localsSize; @@ -114,9 +113,11 @@ this.stackSize = stackSize; final ValueNode[] newValues = new ValueNode[locals.length + stackSize]; for (int i = 0; i < locals.length; i++) { + assert locals[i] == null || !locals[i].isDeleted(); newValues[i] = locals[i]; } for (int i = 0; i < stackSize; i++) { + assert stack[i] == null || !stack[i].isDeleted(); newValues[localsSize + i] = stack[i]; } this.values = new NodeInputList<>(this, newValues); @@ -126,6 +127,10 @@ assert !rethrowException || stackSize == 1 : "must have exception on top of the stack"; } + public NodeIterable values() { + return values; + } + public FrameState outerFrameState() { return outerFrameState; } @@ -135,15 +140,7 @@ this.outerFrameState = x; } - public FrameState outermostFrameState() { - FrameState fs = this; - while (fs.outerFrameState() != null) { - fs = fs.outerFrameState(); - } - return fs; - } - - public void setValueAt(int i, ValueNode x) { + private void setValueAt(int i, ValueNode x) { values.set(i, x); } @@ -199,18 +196,22 @@ return other; } - @Override - public FrameState duplicateWithException(int newBci, ValueNode exceptionObject) { - return duplicateModified(newBci, true, CiKind.Void, exceptionObject); - } - /** * Creates a copy of this frame state with one stack element of type popKind popped from the stack and the * values in pushedValues pushed on the stack. The pushedValues are expected to be in slot encoding: a long * or double is followed by a null slot. */ public FrameState duplicateModified(int newBci, boolean newRethrowException, CiKind popKind, ValueNode... pushedValues) { - int popSlots = popKind == CiKind.Void ? 0 : isTwoSlot(popKind) ? 2 : 1; + int popSlots = 0; + if (popKind != CiKind.Void) { + if (stackAt(stackSize() - 1) == null) { + popSlots = 2; + } else { + popSlots = 1; + } + assert stackAt(stackSize() - popSlots).kind().stackKind() == popKind.stackKind(); + } + int pushSlots = pushedValues.length; FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize - popSlots + pushSlots, newRethrowException, false)); for (int i = 0; i < localsSize; i++) { @@ -228,40 +229,6 @@ return other; } - public boolean isCompatibleWith(FrameStateAccess other) { - if (stackSize() != other.stackSize() || localsSize() != other.localsSize()) { - return false; - } - for (int i = 0; i < stackSize(); i++) { - ValueNode x = stackAt(i); - ValueNode y = other.stackAt(i); - if (x != y && ValueUtil.typeMismatch(x, y)) { - return false; - } - } - if (other.outerFrameState() != outerFrameState()) { - return false; - } - return true; - } - - public boolean equals(FrameStateAccess other) { - if (stackSize() != other.stackSize() || localsSize() != other.localsSize()) { - return false; - } - for (int i = 0; i < stackSize(); i++) { - ValueNode x = stackAt(i); - ValueNode y = other.stackAt(i); - if (x != y) { - return false; - } - } - if (other.outerFrameState() != outerFrameState()) { - return false; - } - return true; - } - /** * Gets the size of the local variables. */ @@ -277,52 +244,14 @@ } /** - * Invalidates the local variable at the specified index. If the specified index refers to a doubleword local, then - * invalidates the high word as well. - * - * @param i the index of the local to invalidate - */ - public void invalidateLocal(int i) { - // note that for double word locals, the high slot should already be null - // unless the local is actually dead and the high slot is being reused; - // in either case, it is not necessary to null the high slot - setValueAt(i, null); - } - - /** - * 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 - */ - // TODO this duplicates code in FrameStateBuilder and needs to go away - public void storeLocal(int i, ValueNode x) { - assert i < localsSize : "local variable index out of range: " + i; - invalidateLocal(i); - setValueAt(i, x); - if (isTwoSlot(x.kind())) { - // (tw) if this was a double word then kill i+1 - setValueAt(i + 1, null); - } - if (i > 0) { - // if there was a double word at i - 1, then kill it - ValueNode p = localAt(i - 1); - if (p != null && isTwoSlot(p.kind())) { - setValueAt(i - 1, null); - } - } - } - - /** * 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 ValueNode localAt(int i) { - assert i < localsSize : "local variable index out of range: " + i; - return valueAt(i); + assert i >= 0 && i < localsSize : "local variable index out of range: " + i; + return values.get(i); } /** @@ -332,233 +261,38 @@ * @return the instruction at the specified position in the stack */ public ValueNode stackAt(int i) { - assert i >= 0 && i < (localsSize + stackSize); - return valueAt(localsSize + i); - } - - /** - * Inserts a phi statement into the stack at the specified stack index. - * @param block the block begin for which we are creating the phi - * @param i the index into the stack for which to create a phi - */ - public PhiNode setupLoopPhiForStack(MergeNode block, int i) { - ValueNode p = stackAt(i); - if (p != null) { - if (p instanceof PhiNode) { - PhiNode phi = (PhiNode) p; - if (phi.merge() == block) { - return phi; - } - } - PhiNode phi = graph().unique(new PhiNode(p.kind(), block, PhiType.Value)); - phi.addInput(p); - setValueAt(localsSize + i, phi); - return phi; - } - return null; - } - - /** - * Inserts a phi statement for the local at the specified index. - * @param block the block begin for which we are creating the phi - * @param i the index of the local variable for which to create the phi - */ - public PhiNode setupLoopPhiForLocal(MergeNode block, int i) { - ValueNode p = localAt(i); - if (p instanceof PhiNode) { - PhiNode phi = (PhiNode) p; - if (phi.merge() == block) { - return phi; - } - } - PhiNode phi = graph().unique(new PhiNode(p.kind(), block, PhiType.Value)); - phi.addInput(p); - storeLocal(i, phi); - return phi; - } - - /** - * Gets the value at a specified index in the set of operand stack and local values represented by this frame. - * This method should only be used to iterate over all the values in this frame, irrespective of whether - * they are on the stack or in local variables. - * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. - * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. - * - * @param i a value in the range {@code [0 .. valuesSize()]} - * @return the value at index {@code i} which may be {@code null} - */ - public ValueNode valueAt(int i) { - assert i < (localsSize + stackSize); - return values.isEmpty() ? null : values.get(i); - } - - /** - * The number of operand stack slots and local variables in this frame. - * This method should typically only be used in conjunction with {@link #valueAt(int)}. - * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. - * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. - * - * @return the number of local variables in this frame - */ - public int valuesSize() { - return localsSize + stackSize; - } - - private boolean checkSize(FrameStateAccess other) { - assert other.stackSize() == stackSize() : "stack sizes do not match"; - assert other.localsSize() == localsSize : "local sizes do not match"; - return true; - } - - public void merge(MergeNode block, FrameStateAccess other) { - assert checkSize(other); - for (int i = 0; i < valuesSize(); i++) { - ValueNode currentValue = valueAt(i); - ValueNode otherValue = other.valueAt(i); - if (currentValue != otherValue || block instanceof LoopBeginNode) { - if (block.isPhiAtMerge(currentValue)) { - addToPhi((PhiNode) currentValue, otherValue, block instanceof LoopBeginNode); - } else { - setValueAt(i, combineValues(currentValue, otherValue, block)); - } - } - } - } - - public void simplifyLoopState() { - for (PhiNode phi : values.filter(PhiNode.class).snapshot()) { - checkRedundantPhi(phi); - } - } - - private static ValueNode combineValues(ValueNode currentValue, ValueNode otherValue, MergeNode block) { - if (currentValue == null || otherValue == null || currentValue.kind() != otherValue.kind()) { - return null; - } - - PhiNode phi = currentValue.graph().add(new PhiNode(currentValue.kind(), block, PhiType.Value)); - for (int j = 0; j < block.phiPredecessorCount(); ++j) { - phi.addInput(currentValue); - } - phi.addInput(otherValue); - assert phi.valueCount() == block.phiPredecessorCount() + 1 : "valueCount=" + phi.valueCount() + " predSize= " + block.phiPredecessorCount(); - return phi; - } - - private static void addToPhi(PhiNode phiNode, ValueNode otherValue, boolean recursiveInvalidCheck) { - if (otherValue == null || otherValue.kind() != phiNode.kind()) { - if (recursiveInvalidCheck) { - deleteInvalidPhi(phiNode); - } else { - phiNode.replaceAtUsages(null); - phiNode.safeDelete(); - } - } else { - phiNode.addInput(otherValue); - } - } - - public static void deleteRedundantPhi(PhiNode redundantPhi, ValueNode phiValue) { - Collection phiUsages = redundantPhi.usages().filter(PhiNode.class).snapshot(); - ((StructuredGraph) redundantPhi.graph()).replaceFloating(redundantPhi, phiValue); - for (PhiNode phi : phiUsages) { - checkRedundantPhi(phi); - } - } - - private static void checkRedundantPhi(PhiNode phiNode) { - if (phiNode.isDeleted() || phiNode.valueCount() == 1) { - return; - } - - ValueNode singleValue = phiNode.singleValue(); - if (singleValue != null) { - deleteRedundantPhi(phiNode, singleValue); - } - } - - private static void deleteInvalidPhi(PhiNode phiNode) { - if (!phiNode.isDeleted()) { - Collection phiUsages = phiNode.usages().filter(PhiNode.class).snapshot(); - phiNode.replaceAtUsages(null); - phiNode.safeDelete(); - for (Node n : phiUsages) { - deleteInvalidPhi((PhiNode) n); - } - } + assert i >= 0 && i < stackSize; + return values.get(localsSize + i); } public MergeNode block() { return usages().filter(MergeNode.class).first(); } - public StateSplit stateSplit() { - return (StateSplit) usages().filterInterface(StateSplit.class).first(); - } - public NodeIterable innerFrameStates() { return usages().filter(FrameState.class); } - /** - * The interface implemented by a client of {@link FrameState#forEachPhi(MergeNode, PhiProcedure)} and - * {@link FrameState#forEachLivePhi(MergeNode, PhiProcedure)}. - */ - public interface PhiProcedure { - boolean doPhi(PhiNode phi); - } - - /** - * Checks whether this frame state has any {@linkplain PhiNode phi} statements. - */ - public boolean hasPhis() { - for (int i = 0; i < valuesSize(); i++) { - ValueNode value = valueAt(i); - if (value instanceof PhiNode) { - return true; - } - } - return false; - } - - public String toDetailedString() { - StringBuilder sb = new StringBuilder(); - String nl = String.format("%n"); - sb.append("[bci: ").append(bci).append("]"); - if (rethrowException()) { - sb.append(" rethrows Exception"); - } - sb.append(nl); - for (int i = 0; i < localsSize(); ++i) { - ValueNode value = localAt(i); - sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); - } - for (int i = 0; i < stackSize(); ++i) { - ValueNode value = stackAt(i); - sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); - } - return sb.toString(); - } - @Override public void generate(LIRGeneratorTool gen) { // Nothing to do, frame states are processed as part of the handling of AbstractStateSplit nodes. } - public static String toString(FrameState frameState) { + private static String toString(FrameState frameState) { StringBuilder sb = new StringBuilder(); String nl = CiUtil.NEW_LINE; FrameState fs = frameState; while (fs != null) { CiUtil.appendLocation(sb, fs.method, fs.bci).append(nl); - for (int i = 0; i < fs.localsSize(); ++i) { - ValueNode value = fs.localAt(i); - sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + sb.append("locals: ["); + for (int i = 0; i < fs.localsSize(); i++) { + sb.append(i == 0 ? "" : ", ").append(fs.localAt(i) == null ? "_" : fs.localAt(i).toString(Verbosity.Id)); } - for (int i = 0; i < fs.stackSize(); ++i) { - ValueNode value = fs.stackAt(i); - sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + sb.append("]").append(nl).append("stack: "); + for (int i = 0; i < fs.stackSize(); i++) { + sb.append(i == 0 ? "" : ", ").append(fs.stackAt(i) == null ? "_" : fs.stackAt(i).toString(Verbosity.Id)); } + sb.append(nl); fs = fs.outerFrameState(); } return sb.toString(); @@ -575,22 +309,6 @@ } } - public void insertLoopPhis(LoopBeginNode loopBegin) { - for (int i = 0; i < stackSize(); i++) { - // always insert phis for the stack - ValueNode x = stackAt(i); - if (x != null) { - setupLoopPhiForStack(loopBegin, i); - } - } - for (int i = 0; i < localsSize(); i++) { - ValueNode x = localAt(i); - if (x != null) { - setupLoopPhiForLocal(loopBegin, i); - } - } - } - @Override public Map getDebugProperties() { Map properties = super.getDebugProperties(); @@ -600,41 +318,27 @@ } else { properties.put("method", "None"); } - StringBuilder str = new StringBuilder(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < localsSize(); i++) { - str.append(i == 0 ? "" : ", ").append(localAt(i) == null ? "_" : localAt(i).toString(Verbosity.Id)); + sb.append(i == 0 ? "" : ", ").append(localAt(i) == null ? "_" : localAt(i).toString(Verbosity.Id)); } - properties.put("locals", str.toString()); - str = new StringBuilder(); + properties.put("locals", sb.toString()); + sb = new StringBuilder(); for (int i = 0; i < stackSize(); i++) { - str.append(i == 0 ? "" : ", ").append(stackAt(i) == null ? "_" : stackAt(i).toString(Verbosity.Id)); + sb.append(i == 0 ? "" : ", ").append(stackAt(i) == null ? "_" : stackAt(i).toString(Verbosity.Id)); } - properties.put("stack", str.toString()); + properties.put("stack", sb.toString()); properties.put("rethrowException", rethrowException); properties.put("duringCall", duringCall); return properties; } - public CiCodePos toCodePos() { - FrameState caller = outerFrameState(); - CiCodePos callerCodePos = null; - if (caller != null) { - callerCodePos = caller.toCodePos(); - } - return new CiCodePos(callerCodePos, method, bci); - } - @Override public boolean verify() { for (ValueNode value : values) { + assert assertTrue(value == null || !value.isDeleted(), "frame state must not contain deleted nodes"); assert assertTrue(value == null || value instanceof VirtualObjectNode || (value.kind() != CiKind.Void && value.kind() != CiKind.Illegal), "unexpected value: %s", value); } return super.verify(); } - - // TODO this duplicates code in FrameStateBuilder and needs to go away - public static boolean isTwoSlot(CiKind kind) { - assert kind != CiKind.Void && kind != CiKind.Illegal; - return kind == CiKind.Long || kind == CiKind.Double; - } } diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/PlaceholderNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/PlaceholderNode.java Wed Mar 07 09:50:36 2012 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +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.nodes; - -import com.oracle.max.graal.graph.*; -import com.oracle.max.graal.nodes.spi.*; -import com.oracle.max.graal.nodes.type.*; - - -public class PlaceholderNode extends AbstractStateSplit implements Node.IterableNodeType, LIRLowerable { - - public PlaceholderNode() { - super(StampFactory.illegal()); - } - - @Override - public void generate(LIRGeneratorTool gen) { - // nothing to do - } -} diff -r e5d42eccfb29 -r d0d0dfbebd03 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/FrameStateAccess.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/FrameStateAccess.java Wed Mar 07 09:50:36 2012 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2012, 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.nodes.spi; - -import com.oracle.max.graal.nodes.*; - -public interface FrameStateAccess { - - FrameState duplicate(int newBci); - - int localsSize(); - - int stackSize(); - - boolean rethrowException(); - - ValueNode valueAt(int i); - - ValueNode localAt(int i); - - ValueNode stackAt(int i); - - FrameState outerFrameState(); - - FrameState duplicateWithException(int bci, ValueNode exceptionObject); - -} diff -r e5d42eccfb29 -r d0d0dfbebd03 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 Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinterObserver.java Wed Mar 07 10:02:33 2012 -0800 @@ -50,6 +50,14 @@ @Override public void dump(Object object, String message) { + try { + dumpSandboxed(object, message); + } catch (Throwable ex) { + TTY.println("CFGPrinter: Exception during output of " + message + ": " + ex); + } + } + + public void dumpSandboxed(Object object, String message) { GraalCompiler compiler = Debug.contextLookup(GraalCompiler.class); if (compiler == null) { return; diff -r e5d42eccfb29 -r d0d0dfbebd03 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 Wed Mar 07 09:50:36 2012 -0800 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinter.java Wed Mar 07 10:02:33 2012 -0800 @@ -32,7 +32,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.java.bytecode.*; import com.oracle.max.graal.lir.cfg.*; import com.oracle.max.graal.nodes.*; diff -r e5d42eccfb29 -r d0d0dfbebd03 mx/commands.py --- a/mx/commands.py Wed Mar 07 09:50:36 2012 -0800 +++ b/mx/commands.py Wed Mar 07 10:02:33 2012 -0800 @@ -484,8 +484,8 @@ build = vmbuild if vmbuild is not None else _vmbuild if _vmSourcesAvailable else 'product' mx.expand_project_in_args(args) - if mx.java().debug: - args = ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000'] + args + if mx.java().debug_port is not None: + args = ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(mx.java().debug_port)] + args if _jacoco == 'on' or _jacoco == 'append': jacocoagent = mx.library("JACOCOAGENT", True) agentOptions = { @@ -646,6 +646,7 @@ parser = ArgumentParser(prog='mx gate'); parser.add_argument('-n', '--omit-native-build', action='store_false', dest='buildNative', help='omit cleaning and building native code') parser.add_argument('-g', '--only-build-graalvm', action='store_false', dest='buildNonGraal', help='only build the Graal VM') + parser.add_argument('--jacocout', help='specify the output directory for jacoco report') args = parser.parse_args(args) @@ -660,7 +661,7 @@ t = Task('BuildJava') build(['--no-native']) tasks.append(t.stop()) - + global _jacoco for vmbuild in ['fastdebug', 'product']: global _vmbuild _vmbuild = vmbuild @@ -674,19 +675,35 @@ vm(['-esa', '-version']) tasks.append(t.stop()) + if vmbuild == 'product' and args.jacocout is not None: + _jacoco = 'on' + t = Task('UnitTests:' + vmbuild) unittest([]) tasks.append(t.stop()) + if vmbuild == 'product' and args.jacocout is not None: + _jacoco = 'append' + t = Task('JavaTesterTests:' + vmbuild) jtt([]) tasks.append(t.stop()) + if vmbuild == 'product' and args.jacocout is not None: + _jacoco = 'off' + for test in sanitycheck.getDacapos(level=sanitycheck.SanityCheckLevel.Gate, gateBuildLevel=vmbuild): t = Task(str(test) + ':' + vmbuild) if not test.test('graal'): t.abort(test.group + ' ' + test.name + ' Failed') tasks.append(t.stop()) + + if args.jacocout is not None: + jacocoreport([args.jacocout]) + + t = Task('BootstrapWithDeoptALot') + vm(['-XX:+DeoptimizeALot', '-XX:+VerifyOops', '-version'], vmbuild='fastdebug') + tasks.append(t.stop()) t = Task('Checkstyle') if mx.checkstyle([]) != 0: diff -r e5d42eccfb29 -r d0d0dfbebd03 mx/projects --- a/mx/projects Wed Mar 07 09:50:36 2012 -0800 +++ b/mx/projects Wed Mar 07 10:02:33 2012 -0800 @@ -1,32 +1,4 @@ -# Library specification format: -# -# library@@= -# -# Library properties (* = required): -# -# *path: the file system path for the library to appear on a class path -# urls: a comma seperated list of URLs from which the library can be downloaded -# optional: if "true" then this library will be omitted from a class path if it doesn't exist on the file system and no URLs are specified -# eclipse.container: the name of the Eclipse library container corresponding to the library -# -# Project specification format: -# -# project@@= -# -# The name of a project also denotes the directory it is in. -# -# Project properties: -# -# *sourceDirs: a comma separated list of source directoriy names (relative to the project directory) -# dependencies: a comma separated list of the libraries and project the project depends upon (transitive dependencies may be omitted) -# eclipse.output: the output directory name (relative to the project directory) -# checkstyle: the project whose Checkstyle configuration (i.e. /.checkstyle_checks.xml) is used -# -# The eclipse.* properties are only used when generating Eclipse project configuration files. -# -# Values can use environment variables with the syntax used in a Bash shell script. -# - +# The format of this file is described in the documentation for my.py. library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar library@JDK_TOOLS@optional=true @@ -55,91 +27,107 @@ project@com.oracle.max.graal.hotspot@sourceDirs=src project@com.oracle.max.graal.hotspot@dependencies=com.oracle.max.graal.snippets project@com.oracle.max.graal.hotspot@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.hotspot@javaCompliance=1.7 # graal.graph project@com.oracle.max.graal.graph@subDir=graal project@com.oracle.max.graal.graph@sourceDirs=src project@com.oracle.max.graal.graph@dependencies=com.oracle.max.graal.debug,JUNIT +project@com.oracle.max.graal.graph@javaCompliance=1.7 # graal.debug project@com.oracle.max.graal.debug@subDir=graal project@com.oracle.max.graal.debug@sourceDirs=src project@com.oracle.max.graal.debug@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.debug@javaCompliance=1.7 # graal.lir project@com.oracle.max.graal.lir@subDir=graal project@com.oracle.max.graal.lir@sourceDirs=src project@com.oracle.max.graal.lir@dependencies=com.oracle.max.asm,com.oracle.max.graal.nodes project@com.oracle.max.graal.lir@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.lir@javaCompliance=1.7 # graal.lir.amd64 project@com.oracle.max.graal.lir.amd64@subDir=graal project@com.oracle.max.graal.lir.amd64@sourceDirs=src project@com.oracle.max.graal.lir.amd64@dependencies=com.oracle.max.graal.lir project@com.oracle.max.graal.lir.amd64@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.lir.amd64@javaCompliance=1.7 # graal.alloc project@com.oracle.max.graal.alloc@subDir=graal project@com.oracle.max.graal.alloc@sourceDirs=src project@com.oracle.max.graal.alloc@dependencies=com.oracle.max.graal.lir project@com.oracle.max.graal.alloc@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.alloc@javaCompliance=1.7 # graal.snippets project@com.oracle.max.graal.snippets@subDir=graal project@com.oracle.max.graal.snippets@sourceDirs=src,test project@com.oracle.max.graal.snippets@dependencies=com.oracle.max.graal.printer project@com.oracle.max.graal.snippets@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.snippets@javaCompliance=1.7 # graal.nodes project@com.oracle.max.graal.nodes@subDir=graal project@com.oracle.max.graal.nodes@sourceDirs=src,test project@com.oracle.max.graal.nodes@dependencies=com.oracle.max.cri,com.oracle.max.graal.graph project@com.oracle.max.graal.nodes@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.nodes@javaCompliance=1.7 # graal.compiler project@com.oracle.max.graal.compiler@subDir=graal project@com.oracle.max.graal.compiler@sourceDirs=src project@com.oracle.max.graal.compiler@dependencies=com.oracle.max.graal.lir.amd64,com.oracle.max.graal.alloc project@com.oracle.max.graal.compiler@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.compiler@javaCompliance=1.7 # 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 +project@com.oracle.max.graal.java@javaCompliance=1.7 # 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.java project@com.oracle.max.graal.printer@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.printer@javaCompliance=1.7 # graal.test project@com.oracle.max.graal.tests@subDir=graal project@com.oracle.max.graal.tests@sourceDirs=src project@com.oracle.max.graal.tests@dependencies=com.oracle.max.graal.printer project@com.oracle.max.graal.tests@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.tests@javaCompliance=1.7 # graal.jtt project@com.oracle.max.graal.jtt@subDir=graal project@com.oracle.max.graal.jtt@sourceDirs=src project@com.oracle.max.graal.jtt@dependencies=JUNIT project@com.oracle.max.graal.jtt@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.graal.jtt@javaCompliance=1.7 # max.asm project@com.oracle.max.asm@subDir=graal project@com.oracle.max.asm@sourceDirs=src project@com.oracle.max.asm@dependencies=com.oracle.max.criutils project@com.oracle.max.asm@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.asm@javaCompliance=1.7 # max.cri project@com.oracle.max.cri@subDir=graal project@com.oracle.max.cri@sourceDirs=src project@com.oracle.max.cri@dependencies= project@com.oracle.max.cri@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.cri@javaCompliance=1.7 # max.criutils project@com.oracle.max.criutils@subDir=graal project@com.oracle.max.criutils@sourceDirs=src project@com.oracle.max.criutils@dependencies=com.oracle.max.cri project@com.oracle.max.criutils@checkstyle=com.oracle.max.graal.graph +project@com.oracle.max.criutils@javaCompliance=1.7 diff -r e5d42eccfb29 -r d0d0dfbebd03 mxtool/mx.py --- a/mxtool/mx.py Wed Mar 07 09:50:36 2012 -0800 +++ b/mxtool/mx.py Wed Mar 07 10:02:33 2012 -0800 @@ -25,64 +25,104 @@ # # ---------------------------------------------------------------------------------------------------- # -# mx is a command line tool inspired by mvn (http://maven.apache.org/) -# and hg (http://mercurial.selenic.com/). It includes a mechanism for -# managing the dependencies between a set of projects (like Maven) -# as well as making it simple to run commands -# (like hg is the interface to the Mercurial commands). -# -# The organizing principle of mx is a project suite. A suite is a directory -# containing one or more projects. It's not coincidental that this closely -# matches the layout of one or more projects in a Mercurial repository. -# The configuration information for a suite lives in an 'mx' sub-directory -# at the top level of the suite. -# -# When launched, mx treats the current working directory as a suite. -# This is the primary suite. All other suites are called included suites. -# -# The configuration files (i.e. in the 'mx' sub-directory) of a suite are: -# -# projects - Defines the projects and libraries in the suite and the dependencies between them -# commands.py - Suite specific extensions to the commands available to mx. This is only processed -# for the primary suite. -# includes - Other suites to be loaded. This is recursive. -# env - A set of environment variable definitions. -# -# The includes and env files are typically not put under version control -# as they usually contain local file-system paths. -# -# The projects file is like the pom.xml file from Maven except that -# it is a properties file (not XML). Each non-comment line -# in the file specifies an attribute of a project or library. The main -# difference between a project and a library is that the former contains -# source code built by the mx tool where as the latter is an external -# dependency. The format of the projects file is -# -# Library specification format: -# -# library@@= -# -# Built-in library properties (* = required): -# -# *path: the file system path for the library to appear on a class path -# urls: a comma seperated list of URLs from which the library can be downloaded -# optional: if "true" then this library will be omitted from a class path if it doesn't exist on the file system and no URLs are specified -# -# Project specification format: -# -# project@@= -# -# The name of a project also denotes the directory it is in. -# -# Built-in project properties: -# -# *sourceDirs: a comma separated list of source directoriy names (relative to the project directory) -# dependencies: a comma separated list of the libraries and project the project depends upon (transitive dependencies may be omitted) -# checkstyle: the project whose Checkstyle configuration (i.e. /.checkstyle_checks.xml) is used -# -# Other properties can be specified for projects and libraries for use by extension commands. -# -# Values can use environment variables with Bash syntax (e.g. ${HOME}). + +r""" +mx is a command line tool inspired by mvn (http://maven.apache.org/) +and hg (http://mercurial.selenic.com/). It includes a mechanism for +managing the dependencies between a set of projects (like Maven) +as well as making it simple to run commands +(like hg is the interface to the Mercurial commands). + +The organizing principle of mx is a project suite. A suite is a directory +containing one or more projects. It's not coincidental that this closely +matches the layout of one or more projects in a Mercurial repository. +The configuration information for a suite lives in an 'mx' sub-directory +at the top level of the suite. + +When launched, mx treats the current working directory as a suite. +This is the primary suite. All other suites are called included suites. + +The configuration files (i.e. in the 'mx' sub-directory) of a suite are: + + projects + Defines the projects and libraries in the suite and the + dependencies between them. + + commands.py + Suite specific extensions to the commands available to mx. + This is only processed for the primary suite. + + includes + Other suites to be loaded. This is recursive. + + env + A set of environment variable definitions. These override any + existing environment variables. + +The includes and env files are typically not put under version control +as they usually contain local file-system paths. + +The projects file is like the pom.xml file from Maven except that +it is a properties file (not XML). Each non-comment line +in the file specifies an attribute of a project or library. The main +difference between a project and a library is that the former contains +source code built by the mx tool where as the latter is an external +dependency. The format of the projects file is + +Library specification format: + + library@@= + +Built-in library properties (* = required): + + *path + The file system path for the library to appear on a class path. + + urls + A comma separated list of URLs from which the library (jar) can + be downloaded and saved in the location specified by 'path'. + + optional + If "true" then this library will be omitted from a class path + if it doesn't exist on the file system and no URLs are specified. + +Project specification format: + + project@@= + +The name of a project also denotes the directory it is in. + +Built-in project properties (* = required): + + subDir + The sub-directory of the suite in which the project directory is + contained. If not specified, the project directory is directly + under the suite directory. + + *sourceDirs + A comma separated list of source directory names (relative to + the project directory). + + dependencies + A comma separated list of the libraries and project the project + depends upon (transitive dependencies should be omitted). + + checkstyle + The project whose Checkstyle configuration + (i.e. /.checkstyle_checks.xml) is used. + + native + "true" if the project is native. + + javaCompliance + The minimum JDK version (format: x.y) to which the project's + sources comply (required for non-native projects). + +Other properties can be specified for projects and libraries for use +by extension commands. + +Property values can use environment variables with Bash syntax (e.g. ${HOME}). +""" import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile, signal import shutil, fnmatch, re, xml.dom.minidom @@ -107,31 +147,32 @@ def __init__(self, suite, name): self.name = name self.suite = suite - + def __str__(self): return self.name - + def __eq__(self, other): return self.name == other.name - + def __ne__(self, other): return self.name != other.name def __hash__(self): return hash(self.name) - + def isLibrary(self): return isinstance(self, Library) - + class Project(Dependency): - def __init__(self, suite, name, srcDirs, deps, dir): + def __init__(self, suite, name, srcDirs, deps, javaCompliance, dir): Dependency.__init__(self, suite, name) self.srcDirs = srcDirs self.deps = deps self.checkstyleProj = name + self.javaCompliance = JavaCompliance(javaCompliance) if javaCompliance is not None else None self.native = False self.dir = dir - + def all_deps(self, deps, includeLibs, includeSelf=True): """ Add the transitive set of dependencies for this project, including @@ -152,7 +193,7 @@ if not self in deps and includeSelf: deps.append(self) return deps - + def _compute_max_dep_distances(self, name, distances, dist): currentDist = distances.get(name); if currentDist is None or currentDist < dist: @@ -161,7 +202,7 @@ if p is not None: for dep in p.deps: self._compute_max_dep_distances(dep, distances, dist + 1) - + def canonical_deps(self): """ Get the dependencies of this project that are not recursive (i.e. cannot be reached @@ -174,19 +215,19 @@ assert d > 0 or n == self.name if d == 1: result.add(n) - - + + if len(result) == len(self.deps) and frozenset(self.deps) == result: return self.deps return result; - + def source_dirs(self): """ Get the directories in which the sources of this project are found. """ return [join(self.dir, s) for s in self.srcDirs] - + def output_dir(self): """ Get the directory in which the class files of this project are found/placed. @@ -195,6 +236,14 @@ return None return join(self.dir, 'bin') + def jasmin_output_dir(self): + """ + Get the directory in which the Jasmin assembled class files of this project are found/placed. + """ + if self.native: + return None + return join(self.dir, 'jasmin_classes') + def append_to_classpath(self, cp, resolve): if not self.native: cp.append(self.output_dir()) @@ -205,7 +254,7 @@ self.path = path.replace('/', os.sep) self.urls = urls self.mustExist = mustExist - + def get_path(self, resolve): path = self.path if not isabs(path): @@ -214,14 +263,14 @@ assert not len(self.urls) == 0, 'cannot find required library ' + self.name + " " + path; print('Downloading ' + self.name + ' from ' + str(self.urls)) download(path, self.urls) - + return path - + def append_to_classpath(self, cp, resolve): path = self.get_path(resolve) if exists(path) or not resolve: cp.append(path) - + class Suite: def __init__(self, dir, primary): self.dir = dir @@ -237,7 +286,7 @@ def _load_projects(self, mxDir): libsMap = dict() - projsMap = dict() + projsMap = dict() projectsFile = join(mxDir, 'projects') if not exists(projectsFile): return @@ -246,9 +295,9 @@ line = line.strip() if len(line) != 0 and line[0] != '#': key, value = line.split('=', 1) - + parts = key.split('@') - + if len(parts) == 2: pass if len(parts) != 3: @@ -260,31 +309,34 @@ m = libsMap else: abort('Property name does not start with "project@" or "library@": ' + key) - + attrs = m.get(name) if attrs is None: attrs = dict() m[name] = attrs value = expandvars_in_property(value) attrs[attr] = value - + def pop_list(attrs, name): v = attrs.pop(name, None) if v is None or len(v.strip()) == 0: return [] return [n.strip() for n in v.split(',')] - + for name, attrs in projsMap.iteritems(): srcDirs = pop_list(attrs, 'sourceDirs') deps = pop_list(attrs, 'dependencies') + javaCompliance = attrs.pop('javaCompliance', None) subDir = attrs.pop('subDir', None); if subDir is None: dir = join(self.dir, name) else: dir = join(self.dir, subDir, name) - p = Project(self, name, srcDirs, deps, dir) + p = Project(self, name, srcDirs, deps, javaCompliance, dir) p.checkstyleProj = attrs.pop('checkstyle', name) p.native = attrs.pop('native', '') == 'true' + if not p.native and p.javaCompliance is None: + abort('javaCompliance property required for non-native project ' + name) p.__dict__.update(attrs) self.projects.append(p) @@ -295,15 +347,15 @@ l = Library(self, name, path, mustExist, urls) l.__dict__.update(attrs) self.libs.append(l) - + def _load_commands(self, mxDir): commands = join(mxDir, 'commands.py') if exists(commands): # temporarily extend the Python path sys.path.insert(0, mxDir) - + mod = __import__('commands') - + # revert the Python path del sys.path[0] @@ -311,17 +363,17 @@ abort(commands + ' must define an mx_init(env) function') if hasattr(mod, 'mx_post_parse_cmd_line'): self.mx_post_parse_cmd_line = mod.mx_post_parse_cmd_line - + mod.mx_init() self.commands = mod - + def _load_includes(self, mxDir): includes = join(mxDir, 'includes') if exists(includes): with open(includes) as f: for line in f: self.includes.append(expandvars_in_property(line.strip())) - + def _load_env(self, mxDir): e = join(mxDir, 'env') if exists(e): @@ -331,7 +383,7 @@ if len(line) != 0 and line[0] != '#': key, value = line.split('=', 1) os.environ[key.strip()] = expandvars_in_property(value.strip()) - + def _post_init(self, opts): mxDir = join(self.dir, 'mx') self._load_includes(mxDir) @@ -348,7 +400,7 @@ if existing is not None: abort('cannot redefine library ' + l.name) _libs[l.name] = l - + def get_os(): """ Get a canonical form of sys.platform. @@ -371,7 +423,7 @@ if not _suites.has_key(dir): suite = Suite(dir, primary) _suites[dir] = suite - return suite + return suite def suites(): """ @@ -384,7 +436,7 @@ Get the list of all loaded projects. """ return _projects.values() - + def project(name, fatalIfMissing=True): """ Get the project for a given name. This will abort if the named project does @@ -421,7 +473,7 @@ path (e.g. downloading a missing library) if 'resolve' is true. """ if names is None: - return _as_classpath(sorted_deps(True), resolve) + return _as_classpath(sorted_deps(includeLibs=True), resolve) deps = [] if isinstance(names, types.StringTypes): project(names).all_deps(deps, True, includeSelf) @@ -429,15 +481,20 @@ for n in names: project(n).all_deps(deps, True, includeSelf) return _as_classpath(deps, resolve) - -def sorted_deps(includeLibs=False): + +def sorted_deps(projectNames=None, includeLibs=False): """ - Gets the loaded projects and libraries sorted such that dependencies + Gets projects and libraries sorted such that dependencies are before the projects that depend on them. Unless 'includeLibs' is true, libraries are omitted from the result. """ deps = [] - for p in _projects.itervalues(): + if projectNames is None: + projects = _projects.values() + else: + projects = [project(name) for name in projectNames] + + for p in projects: p.all_deps(deps, includeLibs) return deps @@ -446,14 +503,15 @@ # Override parent to append the list of available commands def format_help(self): return ArgumentParser.format_help(self) + _format_commands() - - + + def __init__(self): self.java_initialized = False ArgumentParser.__init__(self, prog='mx') - + self.add_argument('-v', action='store_true', dest='verbose', help='enable verbose output') - self.add_argument('-d', action='store_true', dest='java_dbg', help='make Java processes wait on port 8000 for a debugger') + self.add_argument('--dbg', type=int, dest='java_dbg_port', help='make Java processes wait on for a debugger', metavar='') + self.add_argument('-d', action='store_const', const=8000, dest='java_dbg_port', help='alias for "-dbg 8000"') self.add_argument('--cp-pfx', dest='cp_prefix', help='class path prefix', metavar='') self.add_argument('--cp-sfx', dest='cp_suffix', help='class path suffix', metavar='') self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@', default=DEFAULT_JAVA_ARGS) @@ -465,15 +523,15 @@ # Time outs are (currently) implemented with Unix specific functionality self.add_argument('--timeout', help='Timeout (in seconds) for command', type=int, default=0, metavar='') self.add_argument('--ptimeout', help='Timeout (in seconds) for subprocesses', type=int, default=0, metavar='') - + def _parse_cmd_line(self, args=None): if args is None: args = sys.argv[1:] self.add_argument('commandAndArgs', nargs=REMAINDER, metavar='command args...') - + opts = self.parse_args() - + # Give the timeout options a default value to avoid the need for hasattr() tests opts.__dict__.setdefault('timeout', 0) opts.__dict__.setdefault('ptimeout', 0) @@ -486,13 +544,13 @@ if opts.user_home is None or opts.user_home == '': abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.') - + os.environ['JAVA_HOME'] = opts.java_home os.environ['HOME'] = opts.user_home - + commandAndArgs = opts.__dict__.pop('commandAndArgs') return opts, commandAndArgs - + def _format_commands(): msg = '\navailable commands:\n\n' for cmd in sorted(commands.iterkeys()): @@ -531,7 +589,7 @@ if e.errno == errno.EINTR: continue raise - + def _returncode(status): if os.WIFSIGNALED(status): return -os.WTERMSIG(status) @@ -540,7 +598,7 @@ else: # Should never happen raise RuntimeError("Unknown child exit status!") - + end = time.time() + timeout delay = 0.0005 while True: @@ -576,19 +634,19 @@ Each line of the standard output and error streams of the subprocess are redirected to out and err if they are callable objects. """ - + assert isinstance(args, types.ListType), "'args' must be a list: " + str(args) for arg in args: assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg) - + if _opts.verbose: log(' '.join(args)) - + if timeout is None and _opts.ptimeout != 0: timeout = _opts.ptimeout global _currentSubprocess - + try: # On Unix, the new subprocess should be in a separate group so that a timeout alarm # can use os.killpg() to kill the whole subprocess group @@ -597,13 +655,13 @@ if get_os() == 'windows': creationflags = subprocess.CREATE_NEW_PROCESS_GROUP else: - preexec_fn = os.setsid - + preexec_fn = os.setsid + if not callable(out) and not callable(err) and timeout is None: # The preexec_fn=os.setsid p = subprocess.Popen(args, cwd=cwd, preexec_fn=preexec_fn, creationflags=creationflags) _currentSubprocess = (p, args) - retcode = waitOn(p) + retcode = waitOn(p) else: def redirect(stream, f): for line in iter(stream.readline, ''): @@ -641,12 +699,12 @@ if _opts.verbose: raise subprocess.CalledProcessError(retcode, ' '.join(args)) abort(retcode) - + return retcode def exe_suffix(name): """ - Gets the platform specific suffix for an executable + Gets the platform specific suffix for an executable """ if get_os() == 'windows': return name + '.exe' @@ -664,12 +722,30 @@ return name """ +A JavaCompliance simplifies comparing Java compliance values extracted from a JDK version string. +""" +class JavaCompliance: + def __init__(self, ver): + m = re.match('1\.(\d+).*', ver) + assert m is not None, 'not a recognized version string: ' + vstring + self.value = int(m.group(1)) + + def __str__ (self): + return '1.' + str(self.value) + + def __cmp__ (self, other): + if isinstance(other, types.StringType): + other = JavaCompliance(other) + + return cmp(self.value, other.value) + +""" A JavaConfig object encapsulates info on how Java commands are run. """ class JavaConfig: def __init__(self, opts): self.jdk = opts.java_home - self.debug = opts.java_dbg + self.debug_port = opts.java_dbg_port self.java = exe_suffix(join(self.jdk, 'bin', 'java')) self.javac = exe_suffix(join(self.jdk, 'bin', 'javac')) self.javap = exe_suffix(join(self.jdk, 'bin', 'javap')) @@ -679,11 +755,11 @@ def delAtAndSplit(s): return shlex.split(s.lstrip('@')) - + self.java_args = delAtAndSplit(_opts.java_args) self.java_args_pfx = sum(map(delAtAndSplit, _opts.java_args_pfx), []) self.java_args_sfx = sum(map(delAtAndSplit, _opts.java_args_sfx), []) - + # Prepend the -d64 VM option only if the java command supports it try: output = subprocess.check_output([self.java, '-d64', '-version'], stderr=subprocess.STDOUT) @@ -694,17 +770,18 @@ except subprocess.CalledProcessError as e: print e.output abort(e.returncode) - + output = output.split() assert output[1] == 'version' self.version = output[2].strip('"') - - if self.debug: - self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000'] + self.javaCompliance = JavaCompliance(self.version) + + if self.debug_port is not None: + self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(self.debug_port)] def format_cmd(self, args): return [self.java] + self.java_args_pfx + self.java_args + self.java_args_sfx + args - + def check_get_env(key): """ Gets an environment variable, aborting with a useful message if it is not set. @@ -725,7 +802,7 @@ """ Write a message to the console. All script output goes through this method thus allowing a subclass - to redirect it. + to redirect it. """ if msg is None: print @@ -740,7 +817,7 @@ else: cp.append(part) return os.pathsep.join(cp) - + def expand_project_in_args(args): for i in range(len(args)): if args[i] == '-cp' or args[i] == '-classpath': @@ -765,7 +842,7 @@ abort('Property contains an undefined environment variable: ' + value) return result - + def abort(codeOrMessage): """ Aborts the program with a SystemExit exception. @@ -773,7 +850,7 @@ if it is None, the exit status is zero; if it has another type (such as a string), the object's value is printed and the exit status is one. """ - + #import traceback #traceback.print_stack() currentSubprocess = _currentSubprocess @@ -783,7 +860,7 @@ p.kill() else: _kill_process_group(p.pid) - + raise SystemExit(codeOrMessage) def download(path, urls, verbose=False): @@ -795,23 +872,23 @@ d = dirname(path) if d != '' and not exists(d): os.makedirs(d) - + # Try it with the Java tool first since it can show a progress counter myDir = dirname(__file__) - + javaSource = join(myDir, 'URLConnectionDownload.java') javaClass = join(myDir, 'URLConnectionDownload.class') if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource): subprocess.check_call([java().javac, '-d', myDir, javaSource]) if run([java().java, '-cp', myDir, 'URLConnectionDownload', path] + urls) == 0: return - + def url_open(url): userAgent = 'Mozilla/5.0 (compatible)' headers = { 'User-Agent' : userAgent } req = urllib2.Request(url, headers=headers) return urllib2.urlopen(req); - + for url in urls: try: if (verbose): @@ -824,7 +901,7 @@ with contextlib.closing(url_open(url)) as f: data = f.read() zipdata = StringIO.StringIO(f.read()) - + zf = zipfile.ZipFile(zipdata, 'r') data = zf.read(entry) with open(path, 'wb') as f: @@ -839,10 +916,10 @@ log('Error reading from ' + url + ': ' + str(e)) except zipfile.BadZipfile as e: log('Error in zip file downloaded from ' + url + ': ' + str(e)) - + abort('Could not download to ' + path + ' from any of the following URLs:\n\n ' + '\n '.join(urls) + '\n\nPlease use a web browser to do the download manually') - + def update_file(path, content): """ Updates a file with some given content if the content differs from what's in @@ -854,44 +931,47 @@ if existed: with open(path, 'rb') as f: old = f.read() - + if old == content: return False - + with open(path, 'wb') as f: f.write(content) - + log(('modified ' if existed else 'created ') + path) return True; except IOError as e: abort('Error while writing to ' + path + ': ' + str(e)); # Builtin commands - + def build(args, parser=None): """compile the Java and C sources, linking the latter Compile all the Java source code using the appropriate compilers and linkers for the various source code types.""" - + suppliedParser = parser is not None if not suppliedParser: parser = ArgumentParser(prog='mx build') - + + javaCompliance = java().javaCompliance + parser = parser if parser is not None else ArgumentParser(prog='mx build') parser.add_argument('-f', action='store_true', dest='force', help='force compilation even if class files are up to date') parser.add_argument('-c', action='store_true', dest='clean', help='removes existing build output') - parser.add_argument('--source', dest='compliance', help='Java compliance level', default='1.6') + parser.add_argument('--source', dest='compliance', help='Java compliance level', default=str(javaCompliance)) parser.add_argument('--Wapi', action='store_true', dest='warnAPI', help='show warnings about using internal APIs') + parser.add_argument('--projects', action='store', help='comma separated projects to build (omit to build all projects)') parser.add_argument('--no-java', action='store_false', dest='java', help='do not build Java projects') parser.add_argument('--no-native', action='store_false', dest='native', help='do not build native projects') parser.add_argument('--jdt', help='Eclipse installation or path to ecj.jar for using the Eclipse batch compiler instead of javac', metavar='') - + if suppliedParser: parser.add_argument('remainder', nargs=REMAINDER, metavar='...') args = parser.parse_args(args) - + jdtJar = None if args.jdt is not None: if args.jdt.endswith('.jar'): @@ -903,15 +983,19 @@ jdtJar = join(plugins, sorted(choices, reverse=True)[0]) built = set() - for p in sorted_deps(): - + + projects = None + if args.projects is not None: + projects = args.projects.split(',') + + for p in sorted_deps(projects): if p.native: if args.native: log('Calling GNU make {0}...'.format(p.dir)) - + if args.clean: run([gmake_cmd(), 'clean'], cwd=p.dir) - + run([gmake_cmd()], cwd=p.dir) built.add(p.name) continue @@ -919,7 +1003,12 @@ if not args.java: continue - + # skip building this Java project if its Java compliance level is "higher" than the configured JDK + if javaCompliance < p.javaCompliance: + log('Excluding {0} from build (Java compliance level {1} required)'.format(p.name, p.javaCompliance)) + continue + + outputDir = p.output_dir() if exists(outputDir): if args.clean: @@ -936,31 +1025,63 @@ for dep in p.all_deps([], False): if dep.name in built: mustBuild = True - + + jasminAvailable = None javafilelist = [] for sourceDir in sourceDirs: for root, _, files in os.walk(sourceDir): javafiles = [join(root, name) for name in files if name.endswith('.java') and name != 'package-info.java'] javafilelist += javafiles - - # Copy all non Java resources + + # Copy all non Java resources or assemble Jasmin files nonjavafilelist = [join(root, name) for name in files if not name.endswith('.java')] for src in nonjavafilelist: - dst = join(outputDir, src[len(sourceDir) + 1:]) - if exists(dirname(dst)) and (not exists(dst) or os.path.getmtime(dst) != os.path.getmtime(src)): - shutil.copyfile(src, dst) - + if src.endswith('.jasm'): + className = None + with open(src) as f: + for line in f: + if line.startswith('.class '): + className = line.split()[-1] + break + + if className is not None: + jasminOutputDir = p.jasmin_output_dir() + classFile = join(jasminOutputDir, className.replace('/', os.sep) + '.class') + if exists(dirname(classFile)) and (not exists(classFile) or os.path.getmtime(classFile) < os.path.getmtime(src)): + if jasminAvailable is None: + try: + with open(os.devnull) as devnull: + subprocess.call('jasmin', stdout=devnull, stderr=subprocess.STDOUT) + jasminAvailable = True + except OSError as e: + jasminAvailable = False + + if jasminAvailable: + log('Assembling Jasmin file ' + src) + run(['jasmin', '-d', jasminOutputDir, src]) + else: + log('The jasmin executable could not be found - skipping ' + src) + with file(classFile, 'a'): + os.utime(classFile, None) + + else: + log('could not file .class directive in Jasmin source: ' + src) + else: + dst = join(outputDir, src[len(sourceDir) + 1:]) + if exists(dirname(dst)) and (not exists(dst) or os.path.getmtime(dst) != os.path.getmtime(src)): + shutil.copyfile(src, dst) + if not mustBuild: for javafile in javafiles: classfile = outputDir + javafile[len(sourceDir):-len('java')] + 'class' if not exists(classfile) or os.path.getmtime(javafile) > os.path.getmtime(classfile): mustBuild = True break - + if not mustBuild: log('[all class files for {0} are up to date - skipping]'.format(p.name)) continue - + if len(javafilelist) == 0: log('[no Java sources for {0} - skipping]'.format(p.name)) continue @@ -971,7 +1092,7 @@ argfile = open(argfileName, 'wb') argfile.write('\n'.join(javafilelist)) argfile.close() - + try: if jdtJar is None: log('Compiling Java sources for {0} with javac...'.format(p.name)) @@ -981,11 +1102,11 @@ """ Class to errFilt the 'is Sun proprietary API and may be removed in a future release' warning when compiling the VM classes. - + """ def __init__(self): self.c = 0 - + def eat(self, line): if 'proprietary API' in line: self.c = 2 @@ -994,7 +1115,7 @@ else: log(line.rstrip()) errFilt=Filter().eat - + run([java().javac, '-g', '-J-Xmx1g', '-source', args.compliance, '-classpath', cp, '-d', outputDir, '@' + argfile.name], err=errFilt) else: log('Compiling Java sources for {0} with JDT...'.format(p.name)) @@ -1009,7 +1130,7 @@ '-d', outputDir, '@' + argfile.name]) finally: os.remove(argfileName) - + if suppliedParser: return args return None @@ -1018,7 +1139,7 @@ """process all project files to canonicalize the dependencies The exit code of this command reflects how many files were updated.""" - + changedFiles = 0 for s in suites(): projectsFile = join(s.dir, 'mx', 'projects') @@ -1039,7 +1160,7 @@ if update_file(projectsFile, content): changedFiles += 1 return changedFiles; - + def checkstyle(args): """run Checkstyle on the Java sources @@ -1047,16 +1168,16 @@ produced by Checkstyle result in a non-zero exit code. If no projects are given, then all Java projects are checked.""" - + for p in sorted_deps(): if p.native: continue sourceDirs = p.source_dirs() dotCheckstyle = join(p.dir, '.checkstyle') - + if not exists(dotCheckstyle): continue - + for sourceDir in sourceDirs: javafilelist = [] for root, _, files in os.walk(sourceDir): @@ -1075,16 +1196,16 @@ break else: mustCheck = True - + if not mustCheck: log('[all Java sources in {0} already checked - skipping]'.format(sourceDir)) continue - if exists(timestampFile): + if exists(timestampFile): os.utime(timestampFile, None) else: file(timestampFile, 'a') - + dotCheckstyleXML = xml.dom.minidom.parse(dotCheckstyle) localCheckConfig = dotCheckstyleXML.getElementsByTagName('local-check-config')[0] configLocation = localCheckConfig.getAttribute('location') @@ -1102,9 +1223,9 @@ else: log('[unknown Checkstyle configuration type "' + configType + '" in {0} - skipping]'.format(sourceDir)) continue - + exclude = join(p.dir, '.checkstyle.exclude') - + if exists(exclude): with open(exclude) as f: # Convert patterns to OS separators @@ -1112,15 +1233,16 @@ def match(name): for p in patterns: if p in name: - log('excluding: ' + name) + if _opts.verbose: + log('excluding: ' + name) return True return False - + javafilelist = [name for name in javafilelist if not match(name)] - + auditfileName = join(p.dir, 'checkstyleOutput.txt') log('Running Checkstyle on {0} using {1}...'.format(sourceDir, config)) - + try: # Checkstyle is unable to read the filenames to process from a file, and the @@ -1137,7 +1259,7 @@ i += 1 else: break - + batch = javafilelist[:i] javafilelist = javafilelist[i:] try: @@ -1162,13 +1284,13 @@ """ suppliedParser = parser is not None - + parser = parser if suppliedParser else ArgumentParser(prog='mx build'); parser.add_argument('--no-native', action='store_false', dest='native', help='do not clean native projects') parser.add_argument('--no-java', action='store_false', dest='java', help='do not clean Java projects') args = parser.parse_args(args) - + for p in projects(): if p.native: if args.native: @@ -1179,10 +1301,14 @@ if outputDir != '' and exists(outputDir): log('Removing {0}...'.format(outputDir)) shutil.rmtree(outputDir) - + if suppliedParser: return args - + +def about(args): + """show the 'man page' for mx""" + print __doc__ + def help_(args): """show help for a given command @@ -1192,11 +1318,11 @@ if len(args) == 0: _argParser.print_help() return - + name = args[0] if not commands.has_key(name): _argParser.error('unknown command: ' + name) - + value = commands[name] (func, usage) = value[:2] doc = func.__doc__ @@ -1213,7 +1339,7 @@ def projectgraph(args, suite=None): """create dot graph for project structure ("mx projectgraph | dot -Tpdf -oprojects.pdf")""" - + print 'digraph projects {' print 'rankdir=BT;' print 'node [shape=rect];' @@ -1227,19 +1353,28 @@ if suite is None: suite = _mainSuite - + def println(out, obj): out.write(str(obj) + '\n') - + for p in projects(): - if p.native: - continue - if not exists(p.dir): os.makedirs(p.dir) + if p.native: + eclipseNativeSettingsDir = join(suite.dir, 'mx', 'eclipse-native-settings') + if exists(eclipseNativeSettingsDir): + for name in os.listdir(eclipseNativeSettingsDir): + path = join(eclipseNativeSettingsDir, name) + if isfile(path): + with open(join(eclipseNativeSettingsDir, name)) as f: + content = f.read() + content = content.replace('${javaHome}', java().jdk) + update_file(join(p.dir, name), content) + continue + out = StringIO.StringIO() - + println(out, '') println(out, '') for src in p.srcDirs: @@ -1247,14 +1382,14 @@ if not exists(srcDir): os.mkdir(srcDir) println(out, '\t') - + # Every Java program depends on the JRE println(out, '\t') - + for dep in p.all_deps([], True): if dep == p: continue; - + if dep.isLibrary(): if hasattr(dep, 'eclipse.container'): println(out, '\t') @@ -1267,10 +1402,11 @@ if isabs(path): println(out, '\t') else: - println(out, '\t') + projRelPath = os.path.relpath(join(suite.dir, path), p.dir) + println(out, '\t') else: println(out, '\t') - + println(out, '\t') println(out, '') update_file(join(p.dir, '.classpath'), out.getvalue()) @@ -1279,7 +1415,7 @@ csConfig = join(project(p.checkstyleProj).dir, '.checkstyle_checks.xml') if exists(csConfig): out = StringIO.StringIO() - + dotCheckstyle = join(p.dir, ".checkstyle") checkstyleConfigPath = '/' + p.checkstyleProj + '/.checkstyle_checks.xml' println(out, '') @@ -1305,14 +1441,14 @@ assert isdir(exclDir), 'excluded source directory listed in ' + exclude + ' does not exist or is not a directory: ' + exclDir println(out, '\t\t') println(out, '\t') - + println(out, '') update_file(dotCheckstyle, out.getvalue()) out.close() - + out = StringIO.StringIO() - + println(out, '') println(out, '') println(out, '\t' + p.name + '') @@ -1353,6 +1489,7 @@ if isfile(path): with open(join(eclipseSettingsDir, name)) as f: content = f.read() + content = content.replace('${javaCompliance}', str(p.javaCompliance)) update_file(join(settingsDir, name), content) def netbeansinit(args, suite=None): @@ -1363,17 +1500,17 @@ def println(out, obj): out.write(str(obj) + '\n') - + updated = False for p in projects(): if p.native: continue - + if not exists(join(p.dir, 'nbproject')): os.makedirs(join(p.dir, 'nbproject')) out = StringIO.StringIO() - + println(out, '') println(out, '') println(out, '\tBuilds, tests, and runs the project ' + p.name + '.') @@ -1381,7 +1518,7 @@ println(out, '') updated = update_file(join(p.dir, 'build.xml'), out.getvalue()) or updated out.close() - + out = StringIO.StringIO() println(out, '') println(out, '') @@ -1397,18 +1534,18 @@ println(out, ' ') println(out, ' ') println(out, ' ') - + firstDep = True for dep in p.all_deps([], True): if dep == p: continue; - + if not dep.isLibrary(): n = dep.name.replace('.', '_') if firstDep: println(out, ' ') firstDep = False - + println(out, ' ') println(out, ' ' + n + '') println(out, ' jar') @@ -1417,19 +1554,19 @@ println(out, ' clean') println(out, ' jar') println(out, ' ') - + if not firstDep: println(out, ' ') - + println(out, ' ') println(out, '') updated = update_file(join(p.dir, 'nbproject', 'project.xml'), out.getvalue()) or updated out.close() - + out = StringIO.StringIO() - + jdkPlatform = 'JDK_' + java().version - + content = """ annotation.processing.enabled=false annotation.processing.enabled.in.editor=false @@ -1517,12 +1654,12 @@ mainSrc = False else: println(out, 'src.' + src + '.dir=${' + ref + '}') - - javacClasspath = [] + + javacClasspath = [] for dep in p.all_deps([], True): if dep == p: continue; - + if dep.isLibrary(): if not dep.mustExist: continue @@ -1531,22 +1668,22 @@ path = path.replace('\\', '\\\\') ref = 'file.reference.' + dep.name + '-bin' println(out, ref + '=' + path) - + else: n = dep.name.replace('.', '_') relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/') ref = 'reference.' + n + '.jar' println(out, 'project.' + n + '=' + relDepPath) println(out, ref + '=${project.' + n + '}/dist/' + dep.name + '.jar') - + javacClasspath.append('${' + ref + '}') - + println(out, 'javac.classpath=\\\n ' + (os.pathsep + '\\\n ').join(javacClasspath)) - + updated = update_file(join(p.dir, 'nbproject', 'project.properties'), out.getvalue()) or updated out.close() - + if updated: log('If using NetBeans:') log(' 1. Ensure that a platform named "JDK ' + java().version + '" is defined (Tools -> Java Platforms)') @@ -1554,21 +1691,21 @@ def ideclean(args, suite=None): """remove all Eclipse and NetBeans project configurations""" - + def rm(path): if exists(path): os.remove(path) - + for p in projects(): if p.native: continue - + shutil.rmtree(join(p.dir, '.settings'), ignore_errors=True) shutil.rmtree(join(p.dir, 'nbproject'), ignore_errors=True) rm(join(p.dir, '.classpath')) rm(join(p.dir, '.project')) rm(join(p.dir, 'build.xml')) - + def ideinit(args, suite=None): """(re)generate Eclipse and NetBeans project configurations""" eclipseinit(args, suite) @@ -1580,7 +1717,7 @@ Run the JDK javap class file disassembler with the following prepended options: -private -verbose -classpath """ - + javap = java().javap if not exists(javap): abort('The javap executable does not exists: ' + javap) @@ -1602,13 +1739,14 @@ """ assert _argParser is not None _argParser.add_argument(*args, **kwargs) - + # Table of commands in alphabetical order. # Keys are command names, value are lists: [, , ...] # If any of the format args are instances of Callable, then they are called with an 'env' are before being -# used in the call to str.format(). +# used in the call to str.format(). # Extensions should update this table directly commands = { + 'about': [about, ''], 'build': [build, '[options]'], 'checkstyle': [checkstyle, ''], 'canonicalizeprojects': [canonicalizeprojects, ''], @@ -1630,26 +1768,26 @@ if exists(cwdMxDir) and isdir(cwdMxDir): global _mainSuite _mainSuite = _loadSuite(os.getcwd(), True) - + opts, commandAndArgs = _argParser._parse_cmd_line() - + global _opts, _java _opts = opts _java = JavaConfig(opts) - + for s in suites(): s._post_init(opts) - + if len(commandAndArgs) == 0: _argParser.print_help() return - + command = commandAndArgs[0] command_args = commandAndArgs[1:] - + if not commands.has_key(command): abort('mx: unknown command \'{0}\'\n{1}use "mx help" for more options'.format(command, _format_commands())) - + c, _ = commands[command][:2] def term_handler(signum, frame): abort(1)