# HG changeset patch # User Gilles Duboscq # Date 1340799332 -7200 # Node ID 0a249ed5566a94af3e60ac75a11efab96720c03b # Parent 05c5f68e23d58772f659a6074a77b09758b8f39a# Parent 6517d36e6905ec9c715581d1dc41dcb7431f4df9 Merge diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeLookupSwitch.java --- a/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeLookupSwitch.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeLookupSwitch.java Wed Jun 27 14:15:32 2012 +0200 @@ -41,11 +41,6 @@ } @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); } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeSwitch.java --- a/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeSwitch.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeSwitch.java Wed Jun 27 14:15:32 2012 +0200 @@ -80,7 +80,9 @@ * 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(); + public int defaultOffset() { + return stream.readInt(alignedBci); + } /** * Gets the key at {@code i}'th switch target index. @@ -107,4 +109,5 @@ * @return the total size in bytes of the switch instruction */ public abstract int size(); + } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeTableSwitch.java --- a/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeTableSwitch.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.bytecode/src/com/oracle/graal/bytecode/BytecodeTableSwitch.java Wed Jun 27 14:15:32 2012 +0200 @@ -62,11 +62,6 @@ } @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); } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Wed Jun 27 14:15:32 2012 +0200 @@ -187,6 +187,7 @@ // Translating tableswitch instructions public static int SequentialSwitchLimit = 4; public static int RangeTestsSwitchDensity = 5; + public static double MinTableSwitchDensity = 0.5; public static boolean DetailedAsserts = ____; diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/ControlFlowOptimizer.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/ControlFlowOptimizer.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/ControlFlowOptimizer.java Wed Jun 27 14:15:32 2012 +0200 @@ -27,6 +27,7 @@ import com.oracle.graal.compiler.util.*; import com.oracle.graal.debug.*; import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.FallThroughOp; import com.oracle.graal.lir.cfg.*; /** @@ -178,6 +179,16 @@ } } } + // TODO(ls) enable this optimization +// lastOp = instructions.get(instructions.size() - 1); +// if (lastOp instanceof FallThroughOp) { +// FallThroughOp fallThrough = (FallThroughOp) lastOp; +// if (fallThrough.fallThroughTarget() != null && lastOp.info == null) { +// if (fallThrough.fallThroughTarget().label() == ((StandardOp.LabelOp) code.get(i + 1).lir.get(0)).getLabel()) { +// fallThrough.setFallThroughTarget(null); +// } +// } +// } } assert verify(code); diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Wed Jun 27 14:15:32 2012 +0200 @@ -686,7 +686,6 @@ @Override public void emitIf(IfNode x) { - assert x.defaultSuccessor() == x.falseSuccessor() : "wrong destination"; emitBranch(x.compare(), getLIRBlock(x.trueSuccessor()), getLIRBlock(x.falseSuccessor()), null); } @@ -1014,128 +1013,138 @@ } } + /** + * This method tries to create a switch implementation that is optimal for the given switch. + * It will either generate a sequential if/then/else cascade, a set of range tests or a table switch. + * + * If the given switch does not contain int keys, it will always create a sequential implementation. + */ @Override - public void emitLookupSwitch(LookupSwitchNode x) { - Variable tag = load(operand(x.value())); - if (x.numberOfCases() == 0 || x.numberOfCases() < GraalOptions.SequentialSwitchLimit) { - int len = x.numberOfCases(); - for (int i = 0; i < len; i++) { - emitBranch(tag, Constant.forInt(x.keyAt(i)), Condition.EQ, false, getLIRBlock(x.blockSuccessor(i)), null); - } + public void emitSwitch(SwitchNode x) { + int keyCount = x.keyCount(); + if (keyCount == 0) { emitJump(getLIRBlock(x.defaultSuccessor()), null); } else { - visitSwitchRanges(createSwitchRanges(x, null), tag, getLIRBlock(x.defaultSuccessor())); - } - } - - @Override - public void emitTypeSwitch(TypeSwitchNode x) { - Variable tag = load(operand(x.value())); - int len = x.numberOfCases(); - for (int i = 0; i < len; i++) { - ResolvedJavaType type = x.keyAt(i); - emitBranch(tag, type.getEncoding(Representation.ObjectHub), Condition.EQ, false, getLIRBlock(x.blockSuccessor(i)), null); - } - emitJump(getLIRBlock(x.defaultSuccessor()), null); - } - - @Override - public void emitTableSwitch(TableSwitchNode x) { - Variable value = load(operand(x.value())); - // TODO: tune the defaults for the controls used to determine what kind of translation to use - if (x.numberOfCases() == 0 || x.numberOfCases() <= GraalOptions.SequentialSwitchLimit) { - int loKey = x.lowKey(); - int len = x.numberOfCases(); - for (int i = 0; i < len; i++) { - emitBranch(value, Constant.forInt(i + loKey), Condition.EQ, false, getLIRBlock(x.blockSuccessor(i)), null); - } - emitJump(getLIRBlock(x.defaultSuccessor()), null); - } else { - SwitchRange[] switchRanges = createSwitchRanges(null, x); - int rangeDensity = x.numberOfCases() / switchRanges.length; - if (rangeDensity >= GraalOptions.RangeTestsSwitchDensity) { - visitSwitchRanges(switchRanges, value, getLIRBlock(x.defaultSuccessor())); + Variable value = load(operand(x.value())); + LabelRef defaultTarget = x.defaultSuccessor() == null ? null : getLIRBlock(x.defaultSuccessor()); + if (value.kind == Kind.Object || keyCount < GraalOptions.SequentialSwitchLimit) { + // only a few entries + emitSequentialSwitch(x, value, defaultTarget); } else { - LabelRef[] targets = new LabelRef[x.numberOfCases()]; - for (int i = 0; i < x.numberOfCases(); ++i) { - targets[i] = getLIRBlock(x.blockSuccessor(i)); + long valueRange = x.keyAt(keyCount - 1).asLong() - x.keyAt(0).asLong() + 1; + int switchRangeCount = switchRangeCount(x); + int rangeDensity = keyCount / switchRangeCount; + if (rangeDensity >= GraalOptions.RangeTestsSwitchDensity) { + emitSwitchRanges(x, switchRangeCount, value, defaultTarget); + } else if (keyCount / (double) valueRange >= GraalOptions.MinTableSwitchDensity) { + int minValue = x.keyAt(0).asInt(); + assert valueRange < Integer.MAX_VALUE; + LabelRef[] targets = new LabelRef[(int) valueRange]; + for (int i = 0; i < valueRange; i++) { + targets[i] = defaultTarget; + } + for (int i = 0; i < keyCount; i++) { + targets[x.keyAt(i).asInt() - minValue] = getLIRBlock(x.keySuccessor(i)); + } + emitTableSwitch(minValue, defaultTarget, targets, value); + } else { + emitSequentialSwitch(x, value, defaultTarget); } - emitTableSwitch(x.lowKey(), getLIRBlock(x.defaultSuccessor()), targets, value); } } } - protected abstract void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value index); + private void emitSequentialSwitch(final SwitchNode x, Variable key, LabelRef defaultTarget) { + int keyCount = x.keyCount(); + Integer[] indexes = new Integer[keyCount]; + for (int i = 0; i < keyCount; i++) { + indexes[i] = i; + } + Arrays.sort(indexes, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return x.keyProbability(o1) < x.keyProbability(o2) ? 1 : x.keyProbability(o1) > x.keyProbability(o2) ? -1 : 0; + } + }); + LabelRef[] keyTargets = new LabelRef[keyCount]; + Constant[] keyConstants = new Constant[keyCount]; + for (int i = 0; i < keyCount; i++) { + keyTargets[i] = getLIRBlock(x.keySuccessor(indexes[i])); + keyConstants[i] = x.keyAt(indexes[i]); + } + emitSequentialSwitch(keyConstants, keyTargets, defaultTarget, key); + } - // the range of values in a lookupswitch or tableswitch statement - private static final class SwitchRange { - protected final int lowKey; - protected int highKey; - protected final LabelRef sux; + protected abstract void emitSequentialSwitch(Constant[] keyConstants, LabelRef[] keyTargets, LabelRef defaultTarget, Value key); + protected abstract void emitSwitchRanges(int[] lowKeys, int[] highKeys, LabelRef[] targets, LabelRef defaultTarget, Value key); + protected abstract void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value key); - SwitchRange(int lowKey, LabelRef sux) { - this.lowKey = lowKey; - this.highKey = lowKey; - this.sux = sux; + private static int switchRangeCount(SwitchNode x) { + int keyCount = x.keyCount(); + int i = 0; + while (i < keyCount && x.keySuccessorIndex(i) == x.defaultSuccessorIndex()) { + i++; + } + if (i == keyCount) { + return 0; + } else { + int switchRangeCount = 1; + i++; + for (; i < keyCount; i++) { + if (x.keySuccessorIndex(i) != x.defaultSuccessorIndex()) { + if (x.keyAt(i).asInt() != x.keyAt(i - 1).asInt() + 1 || x.keySuccessorIndex(i) != x.keySuccessorIndex(i - 1)) { + switchRangeCount++; + } + } + } + return switchRangeCount; } } - private SwitchRange[] createSwitchRanges(LookupSwitchNode ls, TableSwitchNode ts) { - // Only one of the parameters is used, but code is shared because it is mostly the same. - SwitchNode x = ls != null ? ls : ts; - // we expect the keys to be sorted by increasing value - List res = new ArrayList<>(x.numberOfCases()); - int len = x.numberOfCases(); - if (len > 0) { - LabelRef defaultSux = getLIRBlock(x.defaultSuccessor()); - int key = ls != null ? ls.keyAt(0) : ts.lowKey(); - LabelRef sux = getLIRBlock(x.blockSuccessor(0)); - SwitchRange range = new SwitchRange(key, sux); - for (int i = 1; i < len; i++) { - int newKey = ls != null ? ls.keyAt(i) : key + 1; - LabelRef newSux = getLIRBlock(x.blockSuccessor(i)); - if (key + 1 == newKey && sux == newSux) { - // still in same range - range.highKey = newKey; - } else { - // skip tests which explicitly dispatch to the default - if (range.sux != defaultSux) { - res.add(range); + private void emitSwitchRanges(SwitchNode x, int switchRangeCount, Variable keyValue, LabelRef defaultTarget) { + int[] lowKeys = new int[switchRangeCount]; + int[] highKeys = new int[switchRangeCount]; + LabelRef[] targets = new LabelRef[switchRangeCount]; + + int keyCount = x.keyCount(); + int defaultSuccessor = x.defaultSuccessorIndex(); + + int current = 0; + int i = 0; + while (i < keyCount && x.keySuccessorIndex(i) == x.defaultSuccessorIndex()) { + i++; + } + if (i == keyCount) { + emitJump(defaultTarget, null); + } else { + int key = x.keyAt(i).asInt(); + int successor = x.keySuccessorIndex(i); + lowKeys[current] = key; + highKeys[current] = key; + targets[current] = getLIRBlock(x.blockSuccessor(successor)); + i++; + for (; i < keyCount; i++) { + int newSuccessor = x.keySuccessorIndex(i); + if (newSuccessor != defaultSuccessor) { + int newKey = x.keyAt(i).asInt(); + if (key + 1 == newKey && successor == newSuccessor) { + // still in same range + highKeys[current] = newKey; + } else { + current++; + lowKeys[current] = newKey; + highKeys[current] = newKey; + targets[current] = getLIRBlock(x.blockSuccessor(newSuccessor)); } - range = new SwitchRange(newKey, newSux); + key = newKey; } - key = newKey; - sux = newSux; + successor = newSuccessor; } - if (res.size() == 0 || res.get(res.size() - 1) != range) { - res.add(range); - } + assert current == switchRangeCount - 1; + emitSwitchRanges(lowKeys, highKeys, targets, defaultTarget, keyValue); } - return res.toArray(new SwitchRange[res.size()]); } - private void visitSwitchRanges(SwitchRange[] x, Variable value, LabelRef defaultSux) { - for (int i = 0; i < x.length; i++) { - SwitchRange oneRange = x[i]; - int lowKey = oneRange.lowKey; - int highKey = oneRange.highKey; - LabelRef dest = oneRange.sux; - if (lowKey == highKey) { - emitBranch(value, Constant.forInt(lowKey), Condition.EQ, false, dest, null); - } else if (highKey - lowKey == 1) { - emitBranch(value, Constant.forInt(lowKey), Condition.EQ, false, dest, null); - emitBranch(value, Constant.forInt(highKey), Condition.EQ, false, dest, null); - } else { - Label l = new Label(); - emitBranch(value, Constant.forInt(lowKey), Condition.LT, false, LabelRef.forLabel(l), null); - emitBranch(value, Constant.forInt(highKey), Condition.LE, false, dest, null); - emitLabel(l, false); - } - } - emitJump(defaultSux, null); - } - - protected XirArgument toXirArgument(Value v) { if (v == null) { return null; @@ -1332,68 +1341,6 @@ return emitMove(location); } - SwitchRange[] createLookupRanges(LookupSwitchNode x) { - // we expect the keys to be sorted by increasing value - List res = new ArrayList<>(x.numberOfCases()); - int len = x.numberOfCases(); - if (len > 0) { - LabelRef defaultSux = getLIRBlock(x.defaultSuccessor()); - int key = x.keyAt(0); - LabelRef sux = getLIRBlock(x.blockSuccessor(0)); - SwitchRange range = new SwitchRange(key, sux); - for (int i = 1; i < len; i++) { - int newKey = x.keyAt(i); - LabelRef newSux = getLIRBlock(x.blockSuccessor(i)); - if (key + 1 == newKey && sux == newSux) { - // still in same range - range.highKey = newKey; - } else { - // skip tests which explicitly dispatch to the default - if (range.sux != defaultSux) { - res.add(range); - } - range = new SwitchRange(newKey, newSux); - } - key = newKey; - sux = newSux; - } - if (res.size() == 0 || res.get(res.size() - 1) != range) { - res.add(range); - } - } - return res.toArray(new SwitchRange[res.size()]); - } - - SwitchRange[] createLookupRanges(TableSwitchNode x) { - // TODO: try to merge this with the code for LookupSwitch - List res = new ArrayList<>(x.numberOfCases()); - int len = x.numberOfCases(); - if (len > 0) { - LabelRef sux = getLIRBlock(x.blockSuccessor(0)); - int key = x.lowKey(); - LabelRef defaultSux = getLIRBlock(x.defaultSuccessor()); - SwitchRange range = new SwitchRange(key, sux); - for (int i = 0; i < len; i++, key++) { - LabelRef newSux = getLIRBlock(x.blockSuccessor(i)); - if (sux == newSux) { - // still in same range - range.highKey = key; - } else { - // skip tests which explicitly dispatch to the default - if (sux != defaultSux) { - res.add(range); - } - range = new SwitchRange(key, newSux); - } - sux = newSux; - } - if (res.size() == 0 || res.get(res.size() - 1) != range) { - res.add(range); - } - } - return res.toArray(new SwitchRange[res.size()]); - } - protected XirSupport site(ValueNode x) { return xirSupport.site(x, null); } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/amd64/AMD64LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/amd64/AMD64LIRGenerator.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/amd64/AMD64LIRGenerator.java Wed Jun 27 14:15:32 2012 +0200 @@ -54,12 +54,7 @@ import com.oracle.graal.lir.amd64.AMD64Call.DirectCallOp; import com.oracle.graal.lir.amd64.AMD64Call.IndirectCallOp; import com.oracle.graal.lir.amd64.AMD64Compare.CompareOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.FloatBranchOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.FloatCondMoveOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.ReturnOp; -import com.oracle.graal.lir.amd64.AMD64ControlFlow.TableSwitchOp; +import com.oracle.graal.lir.amd64.AMD64ControlFlow.*; import com.oracle.graal.lir.amd64.AMD64Move.CompareAndSwapOp; import com.oracle.graal.lir.amd64.AMD64Move.LeaOp; import com.oracle.graal.lir.amd64.AMD64Move.LoadOp; @@ -567,9 +562,25 @@ } @Override - protected void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value index) { + protected void emitSequentialSwitch(Constant[] keyConstants, LabelRef[] keyTargets, LabelRef defaultTarget, Value key) { // Making a copy of the switch value is necessary because jump table destroys the input value - Variable tmp = emitMove(index); + if (key.kind == Kind.Int) { + append(new SequentialSwitchOp(keyConstants, keyTargets, defaultTarget, key)); + } else { + assert key.kind == Kind.Object; + append(new SequentialSwitchOp(keyConstants, keyTargets, defaultTarget, key, newVariable(Kind.Object))); + } + } + + @Override + protected void emitSwitchRanges(int[] lowKeys, int[] highKeys, LabelRef[] targets, LabelRef defaultTarget, Value key) { + append(new SwitchRangesOp(lowKeys, highKeys, targets, defaultTarget, key)); + } + + @Override + protected void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value key) { + // Making a copy of the switch value is necessary because jump table destroys the input value + Variable tmp = emitMove(key); append(new TableSwitchOp(lowKey, defaultTarget, targets, tmp, newVariable(target.wordKind))); } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java Wed Jun 27 14:15:32 2012 +0200 @@ -401,7 +401,7 @@ ResolvedJavaType[] types = new ResolvedJavaType[ptypes.length]; double[] probabilities = new double[ptypes.length + 1]; BeginNode[] successors = new BeginNode[ptypes.length + 1]; - + int[] keySuccessors = new int[ptypes.length + 1]; for (int i = 0; i < ptypes.length; i++) { types[i] = ptypes[i].type; probabilities[i] = ptypes[i].probability; @@ -412,12 +412,14 @@ entry = endNode; } successors[i] = BeginNode.begin(entry); + keySuccessors[i] = i; } assert !(unknownTypeSux instanceof MergeNode); successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); probabilities[successors.length - 1] = notRecordedTypeProbability; + keySuccessors[successors.length - 1] = successors.length - 1; - TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(objectClassNode, successors, types, probabilities)); + TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(objectClassNode, successors, probabilities, types, probabilities, keySuccessors)); return typeSwitch; } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Wed Jun 27 14:15:32 2012 +0200 @@ -404,10 +404,15 @@ } private void addSwitchSuccessors(int predBci, BytecodeSwitch bswitch) { + // adds distinct targets to the successor list + Collection targets = new TreeSet<>(); for (int i = 0; i < bswitch.numberOfCases(); i++) { - addSuccessor(predBci, makeBlock(bswitch.targetAt(i))); + targets.add(bswitch.targetAt(i)); } - addSuccessor(predBci, makeBlock(bswitch.defaultTarget())); + targets.add(bswitch.defaultTarget()); + for (int targetBci : targets) { + addSuccessor(predBci, makeBlock(targetBci)); + } } private void addSuccessor(int predBci, Block sux) { diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Wed Jun 27 14:15:32 2012 +0200 @@ -1037,22 +1037,6 @@ appendGoto(createTarget(successor, frameState)); } - private void genTableswitch() { - int bci = bci(); - ValueNode value = frameState.ipop(); - BytecodeTableSwitch ts = new BytecodeTableSwitch(stream(), bci); - - int nofCases = ts.numberOfCases() + 1; // including default case - assert currentBlock.numNormalSuccessors() == nofCases; - - 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, createBlockTarget(probabilities[i], currentBlock.successors.get(i), frameState)); - } - append(tableSwitch); - } - private double[] switchProbability(int numberOfCases, int bci) { double[] prob = profilingInfo.getSwitchProbabilities(bci); if (prob != null) { @@ -1077,22 +1061,35 @@ return true; } - private void genLookupswitch() { + private void genSwitch(BytecodeSwitch bs) { int bci = bci(); ValueNode value = frameState.ipop(); - BytecodeLookupSwitch ls = new BytecodeLookupSwitch(stream(), bci); + + int nofCases = bs.numberOfCases(); - int nofCases = ls.numberOfCases() + 1; // including default case - assert currentBlock.numNormalSuccessors() == nofCases; + Map bciToSuccessorIndex = new HashMap<>(); + int successorCount = currentBlock.successors.size(); + for (int i = 0; i < successorCount; i++) { + assert !bciToSuccessorIndex.containsKey(currentBlock.successors.get(i).startBci); + if (!bciToSuccessorIndex.containsKey(currentBlock.successors.get(i).startBci)) { + bciToSuccessorIndex.put(currentBlock.successors.get(i).startBci, i); + } + } - int[] keys = new int[nofCases - 1]; - for (int i = 0; i < nofCases - 1; ++i) { - keys[i] = ls.keyAt(i); + double[] keyProbabilities = switchProbability(nofCases + 1, bci); + + int[] keys = new int[nofCases]; + int[] keySuccessors = new int[nofCases + 1]; + for (int i = 0; i < nofCases; i++) { + keys[i] = bs.keyAt(i); + keySuccessors[i] = bciToSuccessorIndex.get(bs.targetAt(i)); } - double[] probabilities = switchProbability(nofCases, bci); - LookupSwitchNode lookupSwitch = currentGraph.add(new LookupSwitchNode(value, keys, probabilities)); - for (int i = 0; i < nofCases; ++i) { - lookupSwitch.setBlockSuccessor(i, createBlockTarget(probabilities[i], currentBlock.successors.get(i), frameState)); + keySuccessors[nofCases] = bciToSuccessorIndex.get(bs.defaultTarget()); + + double[] successorProbabilities = IntegerSwitchNode.successorProbabilites(successorCount, keySuccessors, keyProbabilities); + IntegerSwitchNode lookupSwitch = currentGraph.add(new IntegerSwitchNode(value, successorCount, keys, keyProbabilities, keySuccessors)); + for (int i = 0; i < successorCount; i++) { + lookupSwitch.setBlockSuccessor(i, createBlockTarget(successorProbabilities[i], currentBlock.successors.get(i), frameState)); } append(lookupSwitch); } @@ -1691,8 +1688,8 @@ case GOTO : genGoto(); break; case JSR : genJsr(stream.readBranchDest()); break; case RET : genRet(stream.readLocalIndex()); break; - case TABLESWITCH : genTableswitch(); break; - case LOOKUPSWITCH : genLookupswitch(); break; + case TABLESWITCH : genSwitch(new BytecodeTableSwitch(stream(), bci())); break; + case LOOKUPSWITCH : genSwitch(new BytecodeLookupSwitch(stream(), bci())); break; case IRETURN : genReturn(frameState.ipop()); break; case LRETURN : genReturn(frameState.lpop()); break; case FRETURN : genReturn(frameState.fpop()); break; diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java Wed Jun 27 14:15:32 2012 +0200 @@ -35,6 +35,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.FallThroughOp; import com.oracle.graal.lir.asm.*; import com.oracle.graal.nodes.calc.*; @@ -171,6 +172,155 @@ } } + public static class SequentialSwitchOp extends AMD64LIRInstruction implements FallThroughOp { + private final Constant[] keyConstants; + private final LabelRef[] keyTargets; + private LabelRef defaultTarget; + + public SequentialSwitchOp(Constant[] keyConstants, LabelRef[] keyTargets, LabelRef defaultTarget, Value key, Value... temp) { + super("SEQUENTIAL_SWITCH", LIRInstruction.NO_OPERANDS, null, LIRInstruction.NO_OPERANDS, new Value[] {key}, temp); + assert keyConstants.length == keyTargets.length; + this.keyConstants = keyConstants; + this.keyTargets = keyTargets; + this.defaultTarget = defaultTarget; + } + + @Override + public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { + Value key = alive(0); + if (key.kind == Kind.Int) { + Register intKey = asIntReg(key); + for (int i = 0; i < keyConstants.length; i++) { + masm.cmpl(intKey, tasm.asIntConst(keyConstants[i])); + masm.jcc(ConditionFlag.equal, keyTargets[i].label()); + } + } else if (key.kind == Kind.Object) { + Register intKey = asObjectReg(key); + Register temp = asObjectReg(temp(0)); + for (int i = 0; i < keyConstants.length; i++) { + AMD64Move.move(tasm, masm, temp.asValue(Kind.Object), keyConstants[i]); + masm.cmpptr(intKey, temp); + masm.jcc(ConditionFlag.equal, keyTargets[i].label()); + } + } else { + throw new GraalInternalError("sequential switch only supported for int and object"); + } + if (defaultTarget != null) { + masm.jmp(defaultTarget.label()); + } else { + masm.hlt(); + } + } + + @Override + public String operationString() { + StringBuilder buf = new StringBuilder(super.operationString()); + buf.append("\ndefault: [").append(defaultTarget).append(']'); + for (int i = 0; i < keyConstants.length; i++) { + buf.append("\ncase ").append(keyConstants[i]).append(": [").append(keyTargets[i]).append("]"); + } + return buf.toString(); + } + + @Override + protected EnumSet flagsFor(OperandMode mode, int index) { + if (mode == OperandMode.Alive && index == 0) { + return EnumSet.of(OperandFlag.Register); + } else if (mode == OperandMode.Temp && index == 0) { + return EnumSet.of(OperandFlag.Register); + } + throw GraalInternalError.shouldNotReachHere(); + } + + @Override + public LabelRef fallThroughTarget() { + return defaultTarget; + } + + @Override + public void setFallThroughTarget(LabelRef target) { + defaultTarget = target; + } + } + + public static class SwitchRangesOp extends AMD64LIRInstruction implements FallThroughOp { + private final LabelRef[] keyTargets; + private LabelRef defaultTarget; + private final int[] lowKeys; + private final int[] highKeys; + + public SwitchRangesOp(int[] lowKeys, int[] highKeys, LabelRef[] keyTargets, LabelRef defaultTarget, Value key) { + super("SWITCH_RANGES", LIRInstruction.NO_OPERANDS, null, LIRInstruction.NO_OPERANDS, new Value[] {key}, LIRInstruction.NO_OPERANDS); + assert lowKeys.length == keyTargets.length; + assert highKeys.length == keyTargets.length; + assert key.kind == Kind.Int; + this.lowKeys = lowKeys; + this.highKeys = highKeys; + this.keyTargets = keyTargets; + this.defaultTarget = defaultTarget; + } + + @Override + public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { + Register key = asIntReg(alive(0)); + for (int i = 0; i < lowKeys.length; i++) { + int lowKey = lowKeys[i]; + int highKey = highKeys[i]; + if (lowKey == highKey) { + masm.cmpl(key, lowKey); + masm.jcc(ConditionFlag.equal, keyTargets[i].label()); + } else if (lowKey + 1 == highKey) { + masm.cmpl(key, lowKey); + masm.jcc(ConditionFlag.equal, keyTargets[i].label()); + masm.cmpl(key, highKey); + masm.jcc(ConditionFlag.equal, keyTargets[i].label()); + } else { + Label skip = new Label(); + masm.cmpl(key, lowKey); + masm.jcc(ConditionFlag.less, skip); + masm.cmpl(key, highKey); + masm.jcc(ConditionFlag.lessEqual, keyTargets[i].label()); + masm.bind(skip); + } + } + if (defaultTarget != null) { + masm.jmp(defaultTarget.label()); + } else { + masm.hlt(); + } + } + + @Override + public String operationString() { + StringBuilder buf = new StringBuilder(super.operationString()); + buf.append("\ndefault: [").append(defaultTarget).append(']'); + for (int i = 0; i < lowKeys.length; i++) { + buf.append("\ncase ").append(lowKeys[i]).append(" - ").append(highKeys[i]).append(": [").append(keyTargets[i]).append("]"); + } + return buf.toString(); + } + + @Override + protected EnumSet flagsFor(OperandMode mode, int index) { + if (mode == OperandMode.Alive && index == 0) { + return EnumSet.of(OperandFlag.Register); + } else if (mode == OperandMode.Temp && index == 0) { + return EnumSet.of(OperandFlag.Register); + } + throw GraalInternalError.shouldNotReachHere(); + } + + @Override + public LabelRef fallThroughTarget() { + return defaultTarget; + } + + @Override + public void setFallThroughTarget(LabelRef target) { + defaultTarget = target; + } + } + public static class CondMoveOp extends AMD64LIRInstruction { private final ConditionFlag condition; @@ -237,7 +387,6 @@ } } - private static void tableswitch(TargetMethodAssembler tasm, AMD64MacroAssembler masm, int lowKey, LabelRef defaultTarget, LabelRef[] targets, Register value, Register scratch) { Buffer buf = masm.codeBuffer; // Compare index against jump table bounds @@ -251,7 +400,9 @@ } // Jump to default target if index is not within the jump table - masm.jcc(ConditionFlag.above, defaultTarget.label()); + if (defaultTarget != null) { + masm.jcc(ConditionFlag.above, defaultTarget.label()); + } // Set scratch to address of jump table int leaPos = buf.position(); @@ -284,7 +435,7 @@ } else { label.addPatchAt(buf.position()); - buf.emitByte(0); // psuedo-opcode for jump table entry + buf.emitByte(0); // pseudo-opcode for jump table entry buf.emitShort(offsetToJumpTableBase); buf.emitByte(0); // padding to make jump table entry 4 bytes wide } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Wed Jun 27 14:15:32 2012 +0200 @@ -38,6 +38,15 @@ private static Value[] EMPTY = new Value[0]; /** + * Marker interface for LIR ops that can fall through to the next operation, like a switch statement. + * setFallThroughTarget(null) can be used to make the operation fall through to the next one. + */ + public interface FallThroughOp { + LabelRef fallThroughTarget(); + void setFallThroughTarget(LabelRef target); + } + + /** * LIR operation that defines the position of a label. * The first operation of every block must implement this interface. */ diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ControlSplitNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ControlSplitNode.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ControlSplitNode.java Wed Jun 27 14:15:32 2012 +0200 @@ -61,14 +61,6 @@ branchProbability[successorIndex] = x; } - /** - * Gets the successor corresponding to the default (fall through) case. - * @return the default successor - */ - public FixedNode defaultSuccessor() { - return blockSuccessor(blockSuccessorCount() - 1); - } - public Iterable blockSuccessors() { return new Iterable() { @Override diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Wed Jun 27 14:15:32 2012 +0200 @@ -253,7 +253,7 @@ for (int i = 0; i < node.blockSuccessorCount(); i++) { BeginNode successor = node.blockSuccessor(i); node.setBlockSuccessor(i, null); - if (successor != begin && successor.isAlive()) { + if (successor != null && successor != begin && successor.isAlive()) { GraphUtil.killCFG(successor); } } @@ -261,7 +261,7 @@ node.replaceAtPredecessor(begin); node.safeDelete(); } else { - assert node.isDeleted(); + assert node.isDeleted() : node + " " + begin; } } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/IntegerSwitchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/IntegerSwitchNode.java Wed Jun 27 14:15:32 2012 +0200 @@ -0,0 +1,168 @@ +/* + * 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.graal.nodes.extended; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; + +/** + * The {@code IntegerSwitchNode} represents a switch on integer keys, with a sorted array of key values. + * The actual implementation of the switch will be decided by the backend. + */ +public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { + + private final int[] keys; + + /** + * Constructs a integer switch instruction. The keyProbabilities and keySuccessors array contain key.length + 1 + * entries, the last entry describes the default (fall through) case. + * + * @param value the instruction producing the value being switched on + * @param successors the list of successors + * @param keys the sorted list of keys + * @param keyProbabilities the probabilities of the keys + * @param keySuccessor the successor index for each key + */ + public IntegerSwitchNode(ValueNode value, BeginNode[] successors, int[] keys, double[] keyProbabilities, int[] keySuccessors) { + super(value, successors, successorProbabilites(successors.length, keySuccessors, keyProbabilities), keySuccessors, keyProbabilities); + assert keySuccessors.length == keys.length + 1; + assert keySuccessors.length == keyProbabilities.length; + this.keys = keys; + } + + /** + * Constructs a integer switch instruction. The keyProbabilities and keySuccessors array contain key.length + 1 + * entries, the last entry describes the default (fall through) case. + * + * @param value the instruction producing the value being switched on + * @param successorCount the number of successors + * @param keys the sorted list of keys + * @param keyProbabilities the probabilities of the keys + * @param keySuccessor the successor index for each key + */ + public IntegerSwitchNode(ValueNode value, int successorCount, int[] keys, double[] keyProbabilities, int[] keySuccessors) { + this(value, new BeginNode[successorCount], keys, keyProbabilities, keySuccessors); + } + + /** + * Gets the key at the specified index. + * @param i the index + * @return the key at that index + */ + @Override + public Constant keyAt(int i) { + return Constant.forInt(keys[i]); + } + + @Override + public int keyCount() { + return keys.length; + } + + @Override + public void generate(LIRGeneratorTool gen) { + gen.emitSwitch(this); + } + + @Override + public void simplify(SimplifierTool tool) { + if (value() instanceof ConstantNode) { + int constant = value().asConstant().asInt(); + + int survivingEdge = keySuccessorIndex(keyCount()); + for (int i = 0; i < keyCount(); i++) { + if (keys[i] == constant) { + survivingEdge = keySuccessorIndex(i); + } + } + for (int i = 0; i < blockSuccessorCount(); i++) { + if (i != survivingEdge) { + tool.deleteBranch(blockSuccessor(i)); + } + } + tool.addToWorkList(blockSuccessor(survivingEdge)); + ((StructuredGraph) graph()).removeSplitPropagate(this, survivingEdge); + } + if (value() != null) { + IntegerStamp stamp = value().integerStamp(); + if (!stamp.isUnrestricted()) { + int validKeys = 0; + for (int i = 0; i < keyCount(); i++) { + if (stamp.contains(keys[i])) { + validKeys++; + } + } + if (validKeys == 0) { + tool.addToWorkList(defaultSuccessor()); + ((StructuredGraph) graph()).removeSplitPropagate(this, defaultSuccessorIndex()); + } else if (validKeys != keys.length) { + ArrayList newSuccessors = new ArrayList<>(blockSuccessorCount()); + int[] newKeys = new int[validKeys]; + int[] newKeySuccessors = new int [validKeys + 1]; + double[] newKeyProbabilities = new double[validKeys + 1]; + double totalProbability = 0; + int current = 0; + for (int i = 0; i < keyCount() + 1; i++) { + if (i == keyCount() || stamp.contains(keys[i])) { + int index = newSuccessors.indexOf(keySuccessor(i)); + if (index == -1) { + index = newSuccessors.size(); + newSuccessors.add(keySuccessor(i)); + } + newKeySuccessors[current] = index; + if (i < keyCount()) { + newKeys[current] = keys[i]; + } + newKeyProbabilities[current] = keyProbability(i); + totalProbability += keyProbability(i); + current++; + } + } + if (totalProbability > 0) { + for (int i = 0; i < current; i++) { + newKeyProbabilities[i] /= totalProbability; + } + } + + for (int i = 0; i < blockSuccessorCount(); i++) { + BeginNode successor = blockSuccessor(i); + if (!newSuccessors.contains(successor)) { + tool.deleteBranch(successor); + } + setBlockSuccessor(i, null); + } + + BeginNode[] successorsArray = newSuccessors.toArray(new BeginNode[newSuccessors.size()]); + IntegerSwitchNode newSwitch = graph().add(new IntegerSwitchNode(value(), successorsArray, newKeys, newKeyProbabilities, newKeySuccessors)); + ((FixedWithNextNode) predecessor()).setNext(newSwitch); + GraphUtil.killWithUnusedFloatingInputs(this); + } + } + } + } +} diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LookupSwitchNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LookupSwitchNode.java Wed Jun 27 14:15:16 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +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.graal.nodes.extended; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.spi.*; - -/** - * The {@code LookupSwitchNode} represents a lookup switch bytecode, which has a sorted - * array of key values. - */ -public final class LookupSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { - - private final int[] keys; - - /** - * Constructs a new LookupSwitch instruction. - * @param value the instruction producing the value being switched on - * @param successors the list of successors - * @param keys the list of keys, sorted - */ - public LookupSwitchNode(ValueNode value, BeginNode[] successors, int[] keys, double[] probability) { - super(value, successors, probability); - assert successors.length == keys.length + 1; - this.keys = keys; - } - - public LookupSwitchNode(ValueNode value, int[] keys, double[] switchProbability) { - this(value, new BeginNode[switchProbability.length], keys, switchProbability); - } - - /** - * Gets the key at the specified index. - * @param i the index - * @return the key at that index - */ - public int keyAt(int i) { - return keys[i]; - } - - public int keysLength() { - return keys.length; - } - - @Override - public void generate(LIRGeneratorTool gen) { - gen.emitLookupSwitch(this); - } - - @Override - public void simplify(SimplifierTool tool) { - if (value() instanceof ConstantNode) { - ConstantNode constant = (ConstantNode) value(); - int value = constant.value.asInt(); - - int remainingSuxIndex = blockSuccessorCount() - 1; - for (int i = 0; i < keys.length; i++) { - if (value == keys[i]) { - remainingSuxIndex = i; - break; - } - } - - for (int i = 0; i < blockSuccessorCount(); i++) { - if (i != remainingSuxIndex) { - tool.deleteBranch(blockSuccessor(i)); - } - } - - tool.addToWorkList(blockSuccessor(remainingSuxIndex)); - ((StructuredGraph) graph()).removeSplit(this, remainingSuxIndex); - } - } -} diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SwitchNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SwitchNode.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SwitchNode.java Wed Jun 27 14:15:32 2012 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.nodes.extended; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.type.*; @@ -31,6 +33,8 @@ public abstract class SwitchNode extends ControlSplitNode { @Input private ValueNode value; + private final double[] keyProbabilities; + private final int[] keySuccessors; public ValueNode value() { return value; @@ -42,16 +46,72 @@ * @param successors the list of successors of this switch * @param stateAfter the state after the switch */ - public SwitchNode(ValueNode value, BeginNode[] successors, double[] probability) { - super(StampFactory.forVoid(), successors, probability); + public SwitchNode(ValueNode value, BeginNode[] successors, double[] successorProbabilities, int[] keySuccessors, double[] keyProbabilities) { + super(StampFactory.forVoid(), successors, successorProbabilities); + assert keySuccessors.length == keyProbabilities.length; this.value = value; + this.keySuccessors = keySuccessors; + this.keyProbabilities = keyProbabilities; + } + + /** + * The number of distinct keys in this switch. + */ + public abstract int keyCount(); + + /** + * The key at the specified position, encoded in a Constant. + */ + public abstract Constant keyAt(int i); + + /** + * Returns the index of the successor belonging to the key at the specified index. + */ + public int keySuccessorIndex(int i) { + return keySuccessors[i]; + } + + /** + * Returns the successor for the key at the given index. + */ + public BeginNode keySuccessor(int i) { + return blockSuccessor(keySuccessors[i]); } /** - * Gets the number of cases that this switch covers (excluding the default case). - * @return the number of cases + * Returns the probability of the key at the given index. + */ + public double keyProbability(int i) { + return keyProbabilities[i]; + } + + /** + * Returns the index of the default (fall through) successor of this switch. + */ + public int defaultSuccessorIndex() { + return keySuccessors[keySuccessors.length - 1]; + } + + /** + * Gets the successor corresponding to the default (fall through) case. + * @return the default successor */ - public int numberOfCases() { - return blockSuccessorCount() - 1; + public BeginNode defaultSuccessor() { + if (defaultSuccessorIndex() == -1) { + throw new GraalInternalError("unexpected"); + } + return defaultSuccessorIndex() == -1 ? null : blockSuccessor(defaultSuccessorIndex()); + } + + /** + * Helper function that sums up the probabilities of all keys that lead to a specific successor. + * @return an array of size successorCount with the accumulated probability for each successor. + */ + public static double[] successorProbabilites(int successorCount, int[] keySuccessors, double[] keyProbabilities) { + double[] probability = new double[successorCount]; + for (int i = 0; i < keySuccessors.length; i++) { + probability[keySuccessors[i]] += keyProbabilities[i]; + } + return probability; } } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/TableSwitchNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/TableSwitchNode.java Wed Jun 27 14:15:16 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +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.graal.nodes.extended; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.spi.*; - -/** - * The {@code TableSwitchNode} represents a table switch. - */ -public final class TableSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { - - private final int lowKey; - - /** - * Constructs a new TableSwitch instruction. - * @param value the instruction producing the value being switched on - * @param successors the list of successors - * @param lowKey the lowest integer key in the table - */ - public TableSwitchNode(ValueNode value, BeginNode[] successors, int lowKey, double[] probability) { - super(value, successors, probability); - this.lowKey = lowKey; - } - - public TableSwitchNode(ValueNode value, int lowKey, double[] switchProbability) { - this(value, new BeginNode[switchProbability.length], lowKey, switchProbability); - } - - /** - * Gets the lowest key in the table switch (inclusive). - * @return the low key - */ - public int lowKey() { - return lowKey; - } - - /** - * Gets the highest key in the table switch (exclusive). - * @return the high key - */ - public int highKey() { - return lowKey + numberOfCases(); - } - - @Override - public void generate(LIRGeneratorTool gen) { - gen.emitTableSwitch(this); - } - - @Override - public void simplify(SimplifierTool tool) { - if (value() instanceof ConstantNode) { - ConstantNode constant = (ConstantNode) value(); - int value = constant.value.asInt(); - - int remainingSuxIndex; - if (value >= lowKey() && value <= highKey()) { - remainingSuxIndex = value - lowKey(); - } else { - remainingSuxIndex = blockSuccessorCount() - 1; - } - - for (int i = 0; i < blockSuccessorCount(); i++) { - if (i != remainingSuxIndex) { - tool.deleteBranch(blockSuccessor(i)); - } - } - - tool.addToWorkList(blockSuccessor(remainingSuxIndex)); - ((StructuredGraph) graph()).removeSplit(this, remainingSuxIndex); - } - } -} diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeSwitchNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeSwitchNode.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeSwitchNode.java Wed Jun 27 14:15:32 2012 +0200 @@ -22,10 +22,15 @@ */ package com.oracle.graal.nodes.java; +import java.util.*; + import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaType.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; /** * The {@code TypeSwitchNode} performs a lookup based on the type of the input value. @@ -35,32 +40,116 @@ private final ResolvedJavaType[] keys; - public TypeSwitchNode(ValueNode value, BeginNode[] successors, ResolvedJavaType[] keys, double[] probability) { - super(value, successors, probability); + /** + * Constructs a type switch instruction. The keyProbabilities and keySuccessors array contain key.length + 1 + * entries, the last entry describes the default (fall through) case. + * + * @param value the instruction producing the value being switched on + * @param successors the list of successors + * @param keys the list of types + * @param keyProbabilities the probabilities of the keys + * @param keySuccessor the successor index for each key + */ + public TypeSwitchNode(ValueNode value, BeginNode[] successors, double[] successorProbabilities, ResolvedJavaType[] keys, double[] keyProbabilities, int[] keySuccessors) { + super(value, successors, successorProbabilities, keySuccessors, keyProbabilities); assert successors.length == keys.length + 1; - assert successors.length == probability.length; + assert successors.length == keyProbabilities.length; this.keys = keys; } - public TypeSwitchNode(ValueNode value, ResolvedJavaType[] keys, double[] switchProbability) { - this(value, new BeginNode[switchProbability.length], keys, switchProbability); - } - - public ResolvedJavaType keyAt(int i) { - return keys[i]; - } - - public int keysLength() { + @Override + public int keyCount() { return keys.length; } @Override + public Constant keyAt(int i) { + return keys[i].getEncoding(Representation.ObjectHub); + } + + @Override public void generate(LIRGeneratorTool gen) { - gen.emitTypeSwitch(this); + gen.emitSwitch(this); } @Override public void simplify(SimplifierTool tool) { - // TODO(ls) perform simplifications based on the type of value + if (value() instanceof ConstantNode) { + Constant constant = value().asConstant(); + + int survivingEdge = keySuccessorIndex(keyCount()); + for (int i = 0; i < keyCount(); i++) { + Constant typeHub = keyAt(i); + assert constant.kind == typeHub.kind; + if (tool.runtime().areConstantObjectsEqual(value().asConstant(), typeHub)) { + survivingEdge = keySuccessorIndex(i); + } + } + for (int i = 0; i < blockSuccessorCount(); i++) { + if (i != survivingEdge) { + tool.deleteBranch(blockSuccessor(i)); + } + } + tool.addToWorkList(blockSuccessor(survivingEdge)); + ((StructuredGraph) graph()).removeSplitPropagate(this, survivingEdge); + } + if (value() instanceof ReadHubNode) { + ObjectStamp stamp = ((ReadHubNode) value()).object().objectStamp(); + if (stamp.type() != null) { + int validKeys = 0; + for (int i = 0; i < keyCount(); i++) { + if (keys[i].isSubtypeOf(stamp.type())) { + validKeys++; + } + } + if (validKeys == 0) { + tool.addToWorkList(defaultSuccessor()); + ((StructuredGraph) graph()).removeSplitPropagate(this, defaultSuccessorIndex()); + } else if (validKeys != keys.length) { + ArrayList newSuccessors = new ArrayList<>(blockSuccessorCount()); + ResolvedJavaType[] newKeys = new ResolvedJavaType[validKeys]; + int[] newKeySuccessors = new int [validKeys + 1]; + double[] newKeyProbabilities = new double[validKeys + 1]; + double totalProbability = 0; + int current = 0; + for (int i = 0; i < keyCount() + 1; i++) { + if (i == keyCount() || keys[i].isSubtypeOf(stamp.type())) { + int index = newSuccessors.indexOf(keySuccessor(i)); + if (index == -1) { + index = newSuccessors.size(); + newSuccessors.add(keySuccessor(i)); + } + newKeySuccessors[current] = index; + if (i < keyCount()) { + newKeys[current] = keys[i]; + } + newKeyProbabilities[current] = keyProbability(i); + totalProbability += keyProbability(i); + current++; + } + } + if (totalProbability > 0) { + for (int i = 0; i < current; i++) { + newKeyProbabilities[i] /= totalProbability; + } + } + + double[] newSuccessorProbabilities = successorProbabilites(newSuccessors.size(), newKeySuccessors, newKeyProbabilities); + + for (int i = 0; i < blockSuccessorCount(); i++) { + BeginNode successor = blockSuccessor(i); + if (!newSuccessors.contains(successor)) { + tool.deleteBranch(successor); + } + setBlockSuccessor(i, null); + } + + BeginNode[] successorsArray = newSuccessors.toArray(new BeginNode[newSuccessors.size()]); + TypeSwitchNode newSwitch = graph().add(new TypeSwitchNode(value(), successorsArray, newSuccessorProbabilities, newKeys, newKeyProbabilities, newKeySuccessors)); + ((FixedWithNextNode) predecessor()).setNext(newSwitch); + GraphUtil.killWithUnusedFloatingInputs(this); + } + } + } } } diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java Wed Jun 27 14:15:32 2012 +0200 @@ -92,9 +92,7 @@ public abstract void emitConditional(ConditionalNode i); public abstract void emitGuardCheck(BooleanNode comp, DeoptimizationReason deoptReason, DeoptimizationAction deoptAction, boolean negated, long leafGraphId); - public abstract void emitLookupSwitch(LookupSwitchNode i); - public abstract void emitTableSwitch(TableSwitchNode i); - public abstract void emitTypeSwitch(TypeSwitchNode i); + public abstract void emitSwitch(SwitchNode i); public abstract void emitInvoke(Invoke i); public abstract void emitRuntimeCall(RuntimeCallNode i); diff -r 05c5f68e23d5 -r 0a249ed5566a graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/IntegerStamp.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/IntegerStamp.java Wed Jun 27 14:15:16 2012 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/IntegerStamp.java Wed Jun 27 14:15:32 2012 +0200 @@ -73,6 +73,14 @@ return mask; } + public boolean isUnrestricted() { + return lowerBound == kind().minValue() && upperBound == kind().maxValue() && mask == defaultMask(kind()); + } + + public boolean contains(long value) { + return value >= lowerBound && value <= upperBound && (value & mask) == (value & defaultMask(kind())); + } + @Override public String toString() { StringBuilder str = new StringBuilder(); diff -r 05c5f68e23d5 -r 0a249ed5566a graal/overview.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/overview.html Wed Jun 27 14:15:32 2012 +0200 @@ -0,0 +1,42 @@ + + + + + + + + + + + +

+This document is the unified Javadoc for the Graal code base. +The module dependency graph is shown above. +Each node in the diagram is a link to the standalone Javadoc for the denoted module. + + + diff -r 05c5f68e23d5 -r 0a249ed5566a mx/commands.py --- a/mx/commands.py Wed Jun 27 14:15:16 2012 +0200 +++ b/mx/commands.py Wed Jun 27 14:15:32 2012 +0200 @@ -1001,6 +1001,68 @@ mx.abort('jacocoreport takes only one argument : an output directory') mx.run_java(['-jar', jacocoreport.get_path(True), '-in', 'jacoco.exec', '-g', join(_graal_home, 'graal'), out]) +def site(args): + """creates a website containing javadoc and the project dependency graph""" + + parser = ArgumentParser(prog='site') + parser.add_argument('-d', '--base', action='store', help='directory for generated site', required=True, metavar='

') + parser.add_argument('-c', '--clean', action='store_true', help='remove existing site in ') + + args = parser.parse_args(args) + + args.base = os.path.abspath(args.base) + + if not exists(args.base): + os.mkdir(args.base) + elif args.clean: + shutil.rmtree(args.base) + os.mkdir(args.base) + + mx.javadoc(['--base', args.base]) + + unified = join(args.base, 'all') + if exists(unified): + shutil.rmtree(unified) + mx.javadoc(['--base', args.base, '--unified', '--arg', '@-overview', '--arg', '@' + join(_graal_home, 'graal', 'overview.html')]) + os.rename(join(args.base, 'javadoc'), unified) + + _, tmp = tempfile.mkstemp() + try: + svg = join(args.base, 'all', 'modules.svg') + with open(tmp, 'w') as fp: + print >> fp, 'digraph projects {' + print >> fp, 'rankdir=BT;' + print >> fp, 'size = "13,13";' + print >> fp, 'node [shape=rect, fontcolor="blue"];' + print >> fp, 'edge [color="green"];' + for p in mx.projects(): + print >> fp, '"' + p.name + '" [URL = "../' + p.name + '/javadoc/index.html", target = "_top"]' + for dep in p.canonical_deps(): + if mx.project(dep, False): + print >> fp, '"' + p.name + '" -> "' + dep + '"' + depths = dict() + for p in mx.projects(): + d = p.max_depth() + depths.setdefault(d, list()).append(p.name) + for d, names in depths.iteritems(): + print >> fp, '{ rank = same; "' + '"; "'.join(names) + '"; }' + print >> fp, '}' + + mx.run(['dot', '-Tsvg', '-o' + svg, tmp]) + + # Post-process generated SVG to remove unified title elements which most browsers + # render as redundant (and annoying) tooltips. + with open(svg, 'r') as fp: + content = fp.read() + content = re.sub('.*', '', content) + content = re.sub('xlink:title="[^"]*"', '', content) + with open(svg, 'w') as fp: + fp.write(content) + + print 'Created website - root is ' + join(unified, 'index.html') + finally: + os.remove(tmp) + def mx_init(): _vmbuild = 'product' commands = { @@ -1022,6 +1084,7 @@ 'unittest' : [unittest, '[filters...]'], 'jtt' : [jtt, '[filters...]'], 'jacocoreport' : [jacocoreport, '[output directory]'], + 'site' : [site, '[-options]'], 'vm': [vm, '[-options] class [args...]'], 'vmg': [vmg, '[-options] class [args...]'], 'vmfg': [vmfg, '[-options] class [args...]'] diff -r 05c5f68e23d5 -r 0a249ed5566a mxtool/mx.py --- a/mxtool/mx.py Wed Jun 27 14:15:16 2012 +0200 +++ b/mxtool/mx.py Wed Jun 27 14:15:32 2012 +0200 @@ -50,10 +50,10 @@ 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. + Other suites to be loaded. This is recursive. Each + line in an includes file is a path to a suite directory. env A set of environment variable definitions. These override any @@ -223,11 +223,17 @@ if d == 1: result.add(n) - if len(result) == len(self.deps) and frozenset(self.deps) == result: return self.deps return result; + def max_depth(self): + """ + Get the maximum canonical distance between this project and its most distant dependency. + """ + distances = dict() + self._compute_max_dep_distances(self.name, distances, 0) + return max(distances.values()) def source_dirs(self): """ @@ -288,8 +294,8 @@ self.primary = primary mxDir = join(dir, 'mx') self._load_env(mxDir) - if primary: - self._load_commands(mxDir) + self._load_commands(mxDir) + self._load_includes(mxDir) def _load_projects(self, mxDir): libsMap = dict() @@ -360,8 +366,9 @@ if exists(commands): # temporarily extend the Python path sys.path.insert(0, mxDir) - mod = __import__('commands') + + sys.modules[join(mxDir, 'commands')] = sys.modules.pop('commands') # revert the Python path del sys.path[0] @@ -379,7 +386,9 @@ if exists(includes): with open(includes) as f: for line in f: - self.includes.append(expandvars_in_property(line.strip())) + include = expandvars_in_property(line.strip()) + self.includes.append(include) + _loadSuite(include, False) def _load_env(self, mxDir): e = join(mxDir, 'env') @@ -393,9 +402,8 @@ def _post_init(self, opts): mxDir = join(self.dir, 'mx') - self._load_includes(mxDir) self._load_projects(mxDir) - if self.mx_post_parse_cmd_line is not None: + if hasattr(self, 'mx_post_parse_cmd_line'): self.mx_post_parse_cmd_line(opts) for p in self.projects: existing = _projects.get(p.name) @@ -1499,7 +1507,6 @@ print '"' + p.name + '"->"' + dep + '"' print '}' - def _source_locator_memento(deps): slm = XMLDoc() slm.open('sourceLookupDirector') @@ -1950,16 +1957,17 @@ eclipseinit(args, suite) netbeansinit(args, suite) -def javadoc(args): +def javadoc(args, parser=None, docDir='javadoc', includeDeps=True): """generate javadoc for some/all Java projects""" - parser = ArgumentParser(prog='mx javadoc') + parser = ArgumentParser(prog='mx javadoc') if parser is None else parser + parser.add_argument('-d', '--base', action='store', help='base directory for output') parser.add_argument('--unified', action='store_true', help='put javadoc in a single directory instead of one per project') parser.add_argument('--force', action='store_true', help='(re)generate javadoc even if package-list file exists') parser.add_argument('--projects', action='store', help='comma separated projects to process (omit to process all projects)') parser.add_argument('--argfile', action='store', help='name of file containing extra javadoc options') + parser.add_argument('--arg', action='append', dest='extra_args', help='extra Javadoc arguments (e.g. --arg @-use)', metavar='@', default=[]) parser.add_argument('-m', '--memory', action='store', help='-Xmx value to pass to underlying JVM') - parser.add_argument('--wiki', action='store_true', help='generate Confluence Wiki format for package-info.java files') parser.add_argument('--packages', action='store', help='comma separated packages to process (omit to process all packages)') args = parser.parse_args(args) @@ -1969,32 +1977,18 @@ if args.projects is not None: candidates = [project(name) for name in args.projects.split(',')] - # optionally restrict packages within a project (most useful for wiki) + # optionally restrict packages within a project packages = [] if args.packages is not None: packages = [name for name in args.packages.split(',')] - # the WikiDoclet cannot see the -classpath argument passed to javadoc so we pass the - # full list of projects as an explicit argument, thereby enabling it to map classes - # to projects, which is needed to generate Wiki links to the source code. - # There is no virtue in running the doclet on dependent projects as there are - # no generated links between Wiki pages - docletArgs = [] - if args.wiki: - docDir = 'wikidoc' - toolsDir = project('com.oracle.max.tools').output_dir() - baseDir = project('com.oracle.max.base').output_dir() - dp = os.pathsep.join([toolsDir, baseDir]) - project_list = ','.join(p.name for p in sorted_deps()) - docletArgs = ['-docletpath', dp, '-doclet', 'com.oracle.max.tools.javadoc.wiki.WikiDoclet', '-projects', project_list] - else: - docDir = 'javadoc' + def outDir(p): + if args.base is None: + return join(p.dir, docDir) + return join(args.base, p.name, docDir) def check_package_list(p): - if args.wiki: - return True - else: - return not exists(join(p.dir, docDir, 'package-list')) + return not exists(join(outDir(p), 'package-list')) def assess_candidate(p, projects): if p in projects: @@ -2007,7 +2001,7 @@ projects = [] for p in candidates: if not p.native: - if not args.wiki: + if includeDeps: deps = p.all_deps([], includeLibs=False, includeSelf=False) for d in deps: assess_candidate(d, projects) @@ -2024,7 +2018,7 @@ pkgs.add(pkg) return pkgs - extraArgs = [] + extraArgs = [a.lstrip('@') for a in args.extra_args] if args.argfile is not None: extraArgs += ['@' + args.argfile] memory = '2g' @@ -2037,15 +2031,15 @@ pkgs = find_packages(p.source_dirs(), set()) deps = p.all_deps([], includeLibs=False, includeSelf=False) links = ['-link', 'http://docs.oracle.com/javase/' + str(p.javaCompliance.value) + '/docs/api/'] - out = join(p.dir, docDir) + out = outDir(p) for d in deps: - depOut = join(d.dir, docDir) + depOut = outDir(d) links.append('-link') links.append(os.path.relpath(depOut, out)) cp = classpath(p.name, includeSelf=True) sp = os.pathsep.join(p.source_dirs()) log('Generating {2} for {0} in {1}'.format(p.name, out, docDir)) - run([java().javadoc, memory, '-classpath', cp, '-quiet', '-d', out, '-sourcepath', sp] + docletArgs + links + extraArgs + list(pkgs)) + run([java().javadoc, memory, '-classpath', cp, '-quiet', '-d', out, '-sourcepath', sp] + links + extraArgs + list(pkgs)) log('Generated {2} for {0} in {1}'.format(p.name, out, docDir)) else: pkgs = set() @@ -2058,10 +2052,12 @@ links = ['-link', 'http://docs.oracle.com/javase/' + str(_java.javaCompliance.value) + '/docs/api/'] out = join(_mainSuite.dir, docDir) + if args.base is not None: + out = join(args.base, docDir) cp = classpath() sp = os.pathsep.join(sp) log('Generating {2} for {0} in {1}'.format(', '.join(names), out, docDir)) - run([java().javadoc, memory, '-classpath', cp, '-quiet', '-d', out, '-sourcepath', sp] + docletArgs + links + extraArgs + list(pkgs)) + run([java().javadoc, memory, '-classpath', cp, '-quiet', '-d', out, '-sourcepath', sp] + links + extraArgs + list(pkgs)) log('Generated {2} for {0} in {1}'.format(', '.join(names), out, docDir)) def findclass(args): @@ -2132,11 +2128,27 @@ _argParser = ArgParser() +def _findPrimarySuite(): + # try current working directory first + mxDir = join(os.getcwd(), 'mx') + if exists(mxDir) and isdir(mxDir): + return dirname(mxDir) + + # now search path of my executable + me = sys.argv[0] + parent = dirname(me) + while parent: + mxDir = join(parent, 'mx') + if exists(mxDir) and isdir(mxDir): + return parent + parent = dirname(parent) + return None + def main(): - cwdMxDir = join(os.getcwd(), 'mx') - if exists(cwdMxDir) and isdir(cwdMxDir): + primarySuiteDir = _findPrimarySuite() + if primarySuiteDir: global _mainSuite - _mainSuite = _loadSuite(os.getcwd(), True) + _mainSuite = _loadSuite(primarySuiteDir, True) opts, commandAndArgs = _argParser._parse_cmd_line()