# HG changeset patch # User Stefan Anzinger # Date 1430925244 -7200 # Node ID 0927730ed87fbaf1c149d7e3da2f986ac1f666ca # Parent ccddbb1409d2d224a103ddb173a0ea3167029610# Parent bd6f19542e08155194eee4ea2a5bb4880777185f Merge diff -r ccddbb1409d2 -r 0927730ed87f CHANGELOG.md --- a/CHANGELOG.md Wed May 06 17:13:50 2015 +0200 +++ b/CHANGELOG.md Wed May 06 17:14:04 2015 +0200 @@ -4,6 +4,12 @@ ## `tip` +### Graal +* Add experimental support constructing low-level IR in SSA form. +* Add experimental support for SSA linear scan register allocation. +... + +### Truffle ... ## Version 0.7 diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java Wed May 06 17:14:04 2015 +0200 @@ -315,6 +315,9 @@ @Option(help = "Mark well-known stable fields as such.", type = OptionType.Debug) public static final OptionValue ImplicitStableValues = new OptionValue<>(true); + @Option(help = "Generate SSA LIR.", type = OptionType.Debug) + public static final OptionValue SSA_LIR = new OptionValue<>(false); + /** * Counts the various paths taken through snippets. */ diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java --- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Wed May 06 17:14:04 2015 +0200 @@ -59,6 +59,7 @@ import com.oracle.graal.lir.sparc.SPARCMove.MoveFpGpVIS3; import com.oracle.graal.lir.sparc.SPARCMove.MoveFromRegOp; import com.oracle.graal.lir.sparc.SPARCMove.MoveToRegOp; +import com.oracle.graal.lir.sparc.SPARCMove.SPARCStackMove; import com.oracle.graal.lir.sparc.SPARCMove.StackLoadAddressOp; import com.oracle.graal.phases.util.*; import com.oracle.graal.sparc.*; @@ -78,6 +79,11 @@ public LIRInstruction createMove(AllocatableValue result, Value input) { return SPARCLIRGenerator.this.createMove(result, input); } + + @Override + public LIRInstruction createStackMove(AllocatableValue result, Value input) { + return new SPARCStackMove(result, input); + } } public SPARCLIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) { diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/LIRGenerationPhase.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/LIRGenerationPhase.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/LIRGenerationPhase.java Wed May 06 17:14:04 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.compiler; +import static com.oracle.graal.compiler.common.GraalOptions.*; + import java.util.*; import com.oracle.graal.api.code.*; @@ -29,6 +31,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.phases.*; +import com.oracle.graal.lir.ssa.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.spi.*; @@ -60,6 +63,7 @@ emitBlock(nodeLirBuilder, lirGenRes, (Block) b, graph, schedule.getBlockToNodesMap()); } context.lirGen.beforeRegisterAllocation(); + assert !SSA_LIR.getValue() || SSAUtils.verifySSAForm(lirGenRes.getLIR()); } private static void emitBlock(NodeLIRBuilderTool nodeLirGen, LIRGenerationResult lirGenRes, Block b, StructuredGraph graph, BlockMap> blockMap) { diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Wed May 06 17:14:04 2015 +0200 @@ -42,6 +42,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; import com.oracle.graal.lir.StandardOp.JumpOp; +import com.oracle.graal.lir.StandardOp.LabelOp; import com.oracle.graal.lir.debug.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGenerator.Options; @@ -192,6 +193,56 @@ gen.append(op); } + protected LIRKind getExactPhiKind(PhiNode phi) { + ArrayList values = new ArrayList<>(phi.valueCount()); + for (int i = 0; i < phi.valueCount(); i++) { + ValueNode node = phi.valueAt(i); + Value value = node instanceof ConstantNode ? ((ConstantNode) node).asJavaConstant() : getOperand(node); + if (value != null) { + values.add(value); + } else { + assert isPhiInputFromBackedge(phi, i); + } + } + LIRKind derivedKind = LIRKind.merge(values.toArray(new Value[values.size()])); + assert verifyPHIKind(derivedKind, gen.getLIRKind(phi.stamp())); + return derivedKind; + } + + private static boolean verifyPHIKind(LIRKind derivedKind, LIRKind phiKind) { + assert derivedKind.getPlatformKind() != Kind.Object || !derivedKind.isDerivedReference(); + PlatformKind phiPlatformKind = phiKind.getPlatformKind(); + assert derivedKind.getPlatformKind().equals(phiPlatformKind instanceof Kind ? ((Kind) phiPlatformKind).getStackKind() : phiPlatformKind); + return true; + } + + private static boolean isPhiInputFromBackedge(PhiNode phi, int index) { + AbstractMergeNode merge = phi.merge(); + AbstractEndNode end = merge.phiPredecessorAt(index); + return end instanceof LoopEndNode && ((LoopEndNode) end).loopBegin().equals(merge); + } + + private Value[] createPhiIn(AbstractMergeNode merge) { + List values = new ArrayList<>(); + for (ValuePhiNode phi : merge.valuePhis()) { + assert getOperand(phi) == null; + Variable value = gen.newVariable(getExactPhiKind(phi)); + values.add(value); + setResult(phi, value); + } + return values.toArray(new Value[values.size()]); + } + + private Value[] createPhiOut(AbstractMergeNode merge, AbstractEndNode pred) { + List values = new ArrayList<>(); + for (PhiNode phi : merge.valuePhis()) { + Value value = operand(phi.valueAt(pred)); + assert value != null; + values.add(value); + } + return values.toArray(new Value[values.size()]); + } + public void doBlock(Block block, StructuredGraph graph, BlockMap> blockMap) { try (BlockScope blockScope = gen.getBlockScope(block)) { @@ -200,6 +251,19 @@ emitPrologue(graph); } else { assert block.getPredecessorCount() > 0; + if (SSA_LIR.getValue()) { + // create phi-in value array + AbstractBeginNode begin = block.getBeginNode(); + if (begin instanceof AbstractMergeNode) { + AbstractMergeNode merge = (AbstractMergeNode) begin; + LabelOp label = (LabelOp) gen.getResult().getLIR().getLIRforBlock(block).get(0); + label.setIncomingValues(createPhiIn(merge)); + if (Options.PrintIRWithLIR.getValue() && !TTY.isSuppressed()) { + TTY.println("Created PhiIn: " + label); + + } + } + } } List nodes = blockMap.get(block); @@ -347,8 +411,13 @@ @Override public void visitEndNode(AbstractEndNode end) { AbstractMergeNode merge = end.merge(); - moveToPhi(merge, end); - append(newJumpOp(getLIRBlock(merge))); + JumpOp jump = newJumpOp(getLIRBlock(merge)); + if (SSA_LIR.getValue()) { + jump.setOutgoingValues(createPhiOut(merge, end)); + } else { + moveToPhi(merge, end); + } + append(jump); } /** diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Wed May 06 17:14:04 2015 +0200 @@ -233,6 +233,8 @@ String localShortName = getClazz().getSimpleName(); if (localShortName.endsWith("Node") && !localShortName.equals("StartNode") && !localShortName.equals("EndNode")) { shortName = localShortName.substring(0, localShortName.length() - 4); + } else { + shortName = localShortName; } } } diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java Wed May 06 17:14:04 2015 +0200 @@ -65,9 +65,7 @@ protected BytecodeFrame computeFrameForState(FrameState state) { if (isPlaceholderBci(state.bci) && state.bci != BytecodeFrame.BEFORE_BCI) { // This is really a hard error since an incorrect state could crash hotspot - throw new BailoutException(true, "Invalid state " + BytecodeFrame.getPlaceholderBciName(state.bci) + " " + state); -// throw GraalInternalError.shouldNotReachHere("Invalid state " + -// BytecodeFrame.getPlaceholderBciName(state.bci) + " " + state); + throw GraalInternalError.shouldNotReachHere("Invalid state " + BytecodeFrame.getPlaceholderBciName(state.bci) + " " + state); } return super.computeFrameForState(state); } diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/LoopPhiResolutionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/LoopPhiResolutionTest.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.jtt.loop; + +import org.junit.*; + +import com.oracle.graal.api.directives.*; +import com.oracle.graal.jtt.*; + +/* + */ +public class LoopPhiResolutionTest extends JTTTest { + + public static int test(int count) { + int i1 = 1; + int i2 = 2; + int i3 = 3; + int i4 = 4; + + for (int i = 0; i < count; i++) { + i1 = wormhole(i1); + i2 = wormhole(i2); + i3 = wormhole(i2); + i4 = wormhole(i4); + } + return i1 + i2 * 10 + i3 * 100 + i4 * 1000; + } + + private static int wormhole(int x) { + return (int) GraalDirectives.opaque((long) x); + } + + @Test + public void run0() throws Throwable { + runTest("test", 0); + } + + @Test + public void run1() throws Throwable { + runTest("test", 1); + } + + @Test + public void run2() throws Throwable { + runTest("test", 10); + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.jtt; + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.stream.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; +import com.oracle.graal.jtt.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.spi.*; + +/** + * Base class for LIR tests. + *

+ * It provides facilities to replace methods with {@link LIRTestSpecification arbitrary LIR + * instructions}. + */ +public abstract class LIRTest extends JTTTest { + + protected abstract static class LIRTestSpecification { + private Value result; + + void generate(LIRGeneratorTool gen, Value arg0) { + defaultHandler(gen, arg0); + } + + void generate(LIRGeneratorTool gen, Value arg0, Value arg1) { + defaultHandler(gen, arg0, arg1); + } + + void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2) { + defaultHandler(gen, arg0, arg1, arg2); + } + + void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3) { + defaultHandler(gen, arg0, arg1, arg2, arg3); + } + + void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) { + defaultHandler(gen, arg0, arg1, arg2, arg3, arg4); + } + + private static void defaultHandler(@SuppressWarnings("unused") LIRGeneratorTool gen, Value... args) { + throw new GraalInternalError("LIRTestSpecification cannot handle generate() with %d arguments", args.length); + } + + void generate(LIRGeneratorTool gen, Value[] values) { + if (values.length == 1) { + generate(gen, values[0]); + } else if (values.length == 2) { + generate(gen, values[0], values[1]); + } else if (values.length == 3) { + generate(gen, values[0], values[1], values[2]); + } else if (values.length == 4) { + generate(gen, values[0], values[1], values[2], values[3]); + } else if (values.length == 5) { + generate(gen, values[0], values[1], values[2], values[3], values[4]); + } else { + GraalInternalError.unimplemented(); + } + + } + + public void setResult(Value value) { + result = value; + } + + public Value getResult() { + assert result != null : "Result not set (using setResult())"; + return result; + } + } + + @NodeInfo + private static final class FixedLIRTestNode extends FixedWithNextNode implements LIRLowerable { + + public static final NodeClass TYPE = NodeClass.create(FixedLIRTestNode.class); + @Input protected ValueNode opsNode; + @Input protected NodeInputList values; + public final SnippetReflectionProvider snippetReflection; + + public FixedLIRTestNode(SnippetReflectionProvider snippetReflection, ValueNode opsNode, ValueNode[] values) { + super(TYPE, StampFactory.forVoid()); + this.opsNode = opsNode; + this.values = new NodeInputList<>(this, values); + this.snippetReflection = snippetReflection; + } + + public NodeInputList values() { + return values; + } + + public ValueNode getLIROpsNode() { + return opsNode; + } + + @Override + public void generate(NodeLIRBuilderTool gen) { + LIRTestSpecification ops = getLIROperations(); + Stream v = values().stream().map(node -> gen.operand(node)); + + ops.generate(gen.getLIRGeneratorTool(), v.toArray(size -> new Value[size])); + } + + private LIRTestSpecification getLIROperations() { + assert getLIROpsNode().isConstant(); + LIRTestSpecification spec = snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant()); + return spec; + } + } + + @NodeInfo + private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable, Simplifiable { + + public static final NodeClass TYPE = NodeClass.create(FloatingLIRTestNode.class); + @Input protected ValueNode opsNode; + @Input protected NodeInputList values; + public final SnippetReflectionProvider snippetReflection; + + public FloatingLIRTestNode(SnippetReflectionProvider snippetReflection, Kind kind, ValueNode opsNode, ValueNode[] values) { + super(TYPE, StampFactory.forKind(kind)); + this.opsNode = opsNode; + this.values = new NodeInputList<>(this, values); + this.snippetReflection = snippetReflection; + } + + public NodeInputList values() { + return values; + } + + public ValueNode getLIROpsNode() { + return opsNode; + } + + @Override + public void simplify(SimplifierTool tool) { + if (tool.allUsagesAvailable() && getLIROpsNode().isConstant()) { + getLIROpsNode().asConstant(); + } + } + + @Override + public void generate(NodeLIRBuilderTool gen) { + LIRTestSpecification ops = getLIROperations(); + Stream v = values().stream().map(node -> gen.operand(node)); + + ops.generate(gen.getLIRGeneratorTool(), v.toArray(size -> new Value[size])); + gen.setResult(this, ops.getResult()); + } + + private LIRTestSpecification getLIROperations() { + assert getLIROpsNode().isConstant(); + LIRTestSpecification spec = snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant()); + return spec; + } + } + + private InvocationPlugin fixedLIRNodePlugin = new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec) { + b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0) { + b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1) { + b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2) { + b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1, arg2})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2, ValueNode arg3) { + b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1, arg2, arg3})); + return true; + } + }; + + private InvocationPlugin floatingLIRNodePlugin = new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec) { + b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0) { + b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1) { + b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2) { + b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1, arg2})); + return true; + } + + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2, ValueNode arg3) { + b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1, arg2, arg3})); + return true; + } + + }; + + @Override + protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) { + InvocationPlugins invocationPlugins = conf.getPlugins().getInvocationPlugins(); + + Class c = getClass(); + for (Method m : c.getMethods()) { + if (m.getAnnotation(LIRIntrinsic.class) != null) { + assert Modifier.isStatic(m.getModifiers()); + Class[] p = m.getParameterTypes(); + assert p.length > 0; + assert p[0].equals(LIRTestSpecification.class); + + if (m.getReturnType().equals(void.class)) { + invocationPlugins.register(fixedLIRNodePlugin, c, m.getName(), p); + } else { + invocationPlugins.register(floatingLIRNodePlugin, c, m.getName(), p); + } + } + } + return super.editGraphBuilderConfiguration(conf); + } + + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.METHOD) + public static @interface LIRIntrinsic { + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/StackMoveTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/StackMoveTest.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.jtt; + +import org.junit.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.lir.framemap.*; +import com.oracle.graal.lir.gen.*; + +public class StackMoveTest extends LIRTest { + private static final LIRTestSpecification stackCopy = new LIRTestSpecification() { + @Override + public void generate(LIRGeneratorTool gen, Value a) { + FrameMapBuilder frameMapBuilder = gen.getResult().getFrameMapBuilder(); + // create slots + StackSlotValue s1 = frameMapBuilder.allocateSpillSlot(a.getLIRKind()); + StackSlotValue s2 = frameMapBuilder.allocateSpillSlot(a.getLIRKind()); + // move stuff around + gen.emitMove(s1, a); + gen.append(gen.getSpillMoveFactory().createStackMove(s2, s1)); + setResult(gen.emitMove(s2)); + } + }; + + @SuppressWarnings("unused") + @LIRIntrinsic + public static int copyInt(LIRTestSpecification spec, int a) { + return a; + } + + public int testInt(int a) { + return copyInt(stackCopy, a); + } + + @Test + public void runInt() throws Throwable { + runTest("testInt", Integer.MIN_VALUE); + runTest("testInt", -1); + runTest("testInt", 0); + runTest("testInt", 1); + runTest("testInt", Integer.MAX_VALUE); + } + + @SuppressWarnings("unused") + @LIRIntrinsic + public static long copyLong(LIRTestSpecification spec, long a) { + return a; + } + + public long testLong(long a) { + return copyLong(stackCopy, a); + } + + @Test + public void runLong() throws Throwable { + runTest("testLong", Long.MIN_VALUE); + runTest("testLong", -1L); + runTest("testLong", 0L); + runTest("testLong", 1L); + runTest("testLong", Long.MAX_VALUE); + } + + @SuppressWarnings("unused") + @LIRIntrinsic + public static float copyFloat(LIRTestSpecification spec, float a) { + return a; + } + + public float testFloat(float a) { + return copyFloat(stackCopy, a); + } + + @Test + public void runFloat() throws Throwable { + runTest("testFloat", Float.MIN_VALUE); + runTest("testFloat", -1f); + runTest("testFloat", -0.1f); + runTest("testFloat", 0f); + runTest("testFloat", 0.1f); + runTest("testFloat", 1f); + runTest("testFloat", Float.MAX_VALUE); + } + + @SuppressWarnings("unused") + @LIRIntrinsic + public static double copyDouble(LIRTestSpecification spec, double a) { + return a; + } + + public double testDouble(double a) { + return copyDouble(stackCopy, a); + } + + @Test + public void runDouble() throws Throwable { + runTest("testDouble", Double.MIN_VALUE); + runTest("testDouble", -1.); + runTest("testDouble", -0.1); + runTest("testDouble", 0.); + runTest("testDouble", 0.1); + runTest("testDouble", 1.); + runTest("testDouble", Double.MAX_VALUE); + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCMove.java --- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCMove.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCMove.java Wed May 06 17:14:04 2015 +0200 @@ -253,6 +253,62 @@ } } + @Opcode("STACKMOVE") + public static final class SPARCStackMove extends SPARCLIRInstruction implements MoveOp, SPARCTailDelayedLIRInstruction { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SPARCStackMove.class); + + @Def({STACK}) protected AllocatableValue result; + @Use({STACK, HINT}) protected Value input; + + public SPARCStackMove(AllocatableValue result, Value input) { + super(TYPE); + this.result = result; + this.input = input; + } + + @Override + public Value getInput() { + return input; + } + + @Override + public AllocatableValue getResult() { + return result; + } + + @Override + public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { + try (ScratchRegister scratchReg = masm.getScratchRegister()) { + Register scratch = scratchReg.getRegister(); + StackSlot intInput = reInterprete(asStackSlot(getInput())); + StackSlot intResult = reInterprete(asStackSlot(getResult())); + // move stack slot + move(crb, masm, scratch.asValue(intInput.getLIRKind()), intInput, SPARCDelayedControlTransfer.DUMMY); + move(crb, masm, intResult, scratch.asValue(intResult.getLIRKind()), delayedControlTransfer); + } + + } + + private static StackSlot reInterprete(StackSlot slot) { + switch ((Kind) slot.getPlatformKind()) { + case Boolean: + case Byte: + case Short: + case Char: + case Int: + case Long: + case Object: + return slot; + case Float: + return StackSlot.get(LIRKind.value(Kind.Int), slot.getRawOffset(), slot.getRawAddFrameSize()); + case Double: + return StackSlot.get(LIRKind.value(Kind.Long), slot.getRawOffset(), slot.getRawAddFrameSize()); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + } + public abstract static class MemOp extends SPARCLIRInstruction implements ImplicitNullCheck { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(MemOp.class); diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java Wed May 06 17:14:04 2015 +0200 @@ -35,6 +35,7 @@ import com.oracle.graal.lir.LIRInstruction.OperandFlag; import com.oracle.graal.lir.LIRInstruction.OperandMode; import com.oracle.graal.lir.framemap.*; +import com.oracle.graal.lir.ssa.*; public final class LIRVerifier { @@ -128,6 +129,9 @@ LIRInstruction last = lir.getLIRforBlock(block).get(lir.getLIRforBlock(block).size() - 1); assert last instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump"; } + if (block.getPredecessorCount() > 1) { + SSAUtils.verifyPhi(lir, block); + } for (LIRInstruction op : lir.getLIRforBlock(block)) { curInstruction = op; diff -r ccddbb1409d2 -r 0927730ed87f 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 May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Wed May 06 17:14:04 2015 +0200 @@ -23,6 +23,7 @@ package com.oracle.graal.lir; import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*; +import static com.oracle.graal.lir.LIRValueUtil.*; import java.util.*; @@ -87,9 +88,22 @@ public void setIncomingValues(Value[] values) { assert incomingValues.length == 0; + assert values != null; incomingValues = values; } + public int getIncomingSize() { + return incomingValues.length; + } + + public Value getIncomingValue(int idx) { + return incomingValues[idx]; + } + + public void clearIncomingValues() { + incomingValues = NO_VALUES; + } + @Override public void emitCode(CompilationResultBuilder crb) { if (align) { @@ -101,6 +115,13 @@ public Label getLabel() { return label; } + + /** + * @return true if this label acts as a PhiIn. + */ + public boolean isPhiIn() { + return getIncomingSize() > 0 && isVariable(getIncomingValue(0)); + } } /** @@ -109,6 +130,10 @@ public static class JumpOp extends LIRInstruction implements BlockEndOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(JumpOp.class); + private static final Value[] NO_VALUES = new Value[0]; + + @Alive({REG, STACK, CONST}) private Value[] outgoingValues; + private final LabelRef destination; public JumpOp(LabelRef destination) { @@ -118,6 +143,25 @@ protected JumpOp(LIRInstructionClass c, LabelRef destination) { super(c); this.destination = destination; + this.outgoingValues = NO_VALUES; + } + + public void setOutgoingValues(Value[] values) { + assert outgoingValues.length == 0; + assert values != null; + outgoingValues = values; + } + + public int getOutgoingSize() { + return outgoingValues.length; + } + + public Value getOutgoingValue(int idx) { + return outgoingValues[idx]; + } + + public void clearOutgoingValues() { + outgoingValues = NO_VALUES; } @Override diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Wed May 06 17:14:04 2015 +0200 @@ -42,6 +42,7 @@ import com.oracle.graal.lir.*; import com.oracle.graal.lir.LIRInstruction.OperandFlag; import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.LabelOp; import com.oracle.graal.lir.StandardOp.MoveOp; import com.oracle.graal.lir.alloc.lsra.Interval.RegisterBinding; import com.oracle.graal.lir.alloc.lsra.Interval.RegisterPriority; @@ -57,7 +58,7 @@ * >"Optimized Interval Splitting in a Linear Scan Register Allocator" by Christian Wimmer and * Hanspeter Moessenboeck. */ -final class LinearScan { +class LinearScan { final TargetDescription target; final LIRGenerationResult res; @@ -201,7 +202,9 @@ } protected MoveResolver createMoveResolver() { - return new MoveResolver(this); + MoveResolver moveResolver = new MoveResolver(this); + assert moveResolver.checkEmpty(); + return moveResolver; } public static boolean isVariableOrRegister(Value value) { @@ -522,6 +525,15 @@ } }; + /** + * @return the index of the first instruction that is of interest for + * {@link #eliminateSpillMoves()} + */ + protected int firstInstructionOfInterest() { + // skip the first because it is always a label + return 1; + } + // called once before assignment of register numbers void eliminateSpillMoves() { try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves")) { @@ -536,67 +548,78 @@ LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer(); for (AbstractBlockBase block : sortedBlocks) { - List instructions = ir.getLIRforBlock(block); - int numInst = instructions.size(); - - // iterate all instructions of the block. skip the first - // because it is always a label - for (int j = 1; j < numInst; j++) { - LIRInstruction op = instructions.get(j); - int opId = op.id(); + try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) { + List instructions = ir.getLIRforBlock(block); + int numInst = instructions.size(); - if (opId == -1) { - MoveOp move = (MoveOp) op; - // remove move from register to stack if the stack slot is guaranteed to be - // correct. - // only moves that have been inserted by LinearScan can be removed. - assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables"; - - Interval curInterval = intervalFor(move.getResult()); + // iterate all instructions of the block. + for (int j = firstInstructionOfInterest(); j < numInst; j++) { + LIRInstruction op = instructions.get(j); + int opId = op.id(); - if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { - // move target is a stack slot that is always correct, so eliminate - // instruction - if (Debug.isLogEnabled()) { - Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); - } - // null-instructions are deleted by assignRegNum - instructions.set(j, null); - } - - } else { - // insert move from register to stack just after - // the beginning of the interval - assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order"; - assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; - - while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { - if (!interval.canMaterialize()) { - if (!insertionBuffer.initialized()) { - // prepare insertion buffer (appended when all instructions in - // the block are processed) - insertionBuffer.init(instructions); + if (opId == -1) { + MoveOp move = (MoveOp) op; + /* + * Remove move from register to stack if the stack slot is guaranteed to + * be correct. Only moves that have been inserted by LinearScan can be + * removed. + */ + if (canEliminateSpillMove(block, move)) { + /* + * Move target is a stack slot that is always correct, so eliminate + * instruction. + */ + if (Debug.isLogEnabled()) { + Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", operandNumber(move.getInput()), move.getInput(), operandNumber(move.getResult()), + move.getResult(), block); } - AllocatableValue fromLocation = interval.location(); - AllocatableValue toLocation = canonicalSpillOpr(interval); + // null-instructions are deleted by assignRegNum + instructions.set(j, null); + } - assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState(); - assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; + } else { + /* + * Insert move from register to stack just after the beginning of the + * interval. + */ + assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order"; + assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; - insertionBuffer.append(j + 1, getSpillMoveFactory().createMove(toLocation, fromLocation)); + while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { + if (!interval.canMaterialize()) { + if (!insertionBuffer.initialized()) { + /* + * prepare insertion buffer (appended when all instructions + * in the block are processed) + */ + insertionBuffer.init(instructions); + } - if (Debug.isLogEnabled()) { - Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); + AllocatableValue fromLocation = interval.location(); + AllocatableValue toLocation = canonicalSpillOpr(interval); + if (!fromLocation.equals(toLocation)) { + + assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + + interval.spillState(); + assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; + + LIRInstruction move = getSpillMoveFactory().createMove(toLocation, fromLocation); + insertionBuffer.append(j + 1, move); + + if (Debug.isLogEnabled()) { + Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); + } + } } + interval = interval.next; } - interval = interval.next; } - } - } // end of instruction iteration + } // end of instruction iteration - if (insertionBuffer.initialized()) { - insertionBuffer.finish(); + if (insertionBuffer.initialized()) { + insertionBuffer.finish(); + } } } // end of block iteration @@ -604,6 +627,22 @@ } } + /** + * @param block The block {@code move} is located in. + * @param move Spill move. + */ + protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { + assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; + + Interval curInterval = intervalFor(move.getResult()); + + if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { + assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); + return true; + } + return false; + } + private static void checkIntervals(Interval interval) { Interval prev = null; Interval temp = interval; @@ -1058,7 +1097,7 @@ } changeSpillDefinitionPos(interval, defPos); - if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal()) { + if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { // detection of method-parameters and roundfp-results interval.setSpillState(SpillState.StartInMemory); } @@ -1078,6 +1117,11 @@ if (optimizeMethodArgument(move.getInput())) { return RegisterPriority.None; } + } else if (op instanceof LabelOp) { + LabelOp label = (LabelOp) op; + if (label.isPhiIn()) { + return RegisterPriority.None; + } } // all other operands require a register @@ -1456,8 +1500,11 @@ throw new BailoutException("LinearScan: interval is null"); } - void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, MoveResolver moveResolver) { + void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { assert moveResolver.checkEmpty(); + assert midBlock == null || + (midBlock.getPredecessorCount() == 1 && midBlock.getSuccessorCount() == 1 && midBlock.getPredecessors().get(0).equals(fromBlock) && midBlock.getSuccessors().get(0).equals( + toBlock)); int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock); int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1; @@ -1551,7 +1598,7 @@ // directly resolve between pred and sux (without looking // at the empty block // between) - resolveCollectMappings(pred, sux, moveResolver); + resolveCollectMappings(pred, sux, block, moveResolver); if (moveResolver.hasMappings()) { moveResolver.setInsertPosition(instructions, 1); moveResolver.resolveAndAppendMoves(); @@ -1579,7 +1626,7 @@ // collect all intervals that have been split between // fromBlock and toBlock - resolveCollectMappings(fromBlock, toBlock, moveResolver); + resolveCollectMappings(fromBlock, toBlock, null, moveResolver); if (moveResolver.hasMappings()) { resolveFindInsertPos(fromBlock, toBlock, moveResolver); moveResolver.resolveAndAppendMoves(); @@ -1588,6 +1635,7 @@ } } } + } } @@ -1837,6 +1885,8 @@ verify(); } + beforeSpillMoveElimination(); + try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = eliminateSpillMoveTimer.start()) { eliminateSpillMoves(); } catch (Throwable e) { @@ -1861,6 +1911,9 @@ } } + protected void beforeSpillMoveElimination() { + } + private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition"); private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability"); diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Wed May 06 17:14:04 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.lir.alloc.lsra; +import static com.oracle.graal.compiler.common.GraalOptions.*; + import java.util.*; import com.oracle.graal.api.code.*; @@ -30,12 +32,28 @@ import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; import com.oracle.graal.lir.phases.*; +import com.oracle.graal.lir.ssa.*; +import com.oracle.graal.options.*; +import com.oracle.graal.options.DerivedOptionValue.OptionSupplier; public final class LinearScanPhase extends AllocationPhase { + public static final DerivedOptionValue SSA_LSRA = new DerivedOptionValue<>(new OptionSupplier() { + + private static final long serialVersionUID = 9115795480259228194L; + + public Boolean get() { + return SSA_LIR.getValue() && !SSADestructionPhase.Options.LIREagerSSADestruction.getValue(); + } + }); + @Override protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { - new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); + if (LinearScanPhase.SSA_LSRA.getValue()) { + new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); + } else { + new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); + } } } diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Wed May 06 17:14:04 2015 +0200 @@ -35,7 +35,7 @@ /** */ -final class MoveResolver { +class MoveResolver { private final LinearScan allocator; @@ -48,14 +48,16 @@ private boolean multipleReadsAllowed; private final int[] registerBlocked; - private void setValueBlocked(Value location, int direction) { + protected void setValueBlocked(Value location, int direction) { assert direction == 1 || direction == -1 : "out of bounds"; if (isRegister(location)) { registerBlocked[asRegister(location).number] += direction; + } else { + throw GraalInternalError.shouldNotReachHere("unhandled value " + location); } } - private int valueBlocked(Value location) { + protected int valueBlocked(Value location) { if (isRegister(location)) { return registerBlocked[asRegister(location).number]; } @@ -66,10 +68,18 @@ multipleReadsAllowed = true; } + protected boolean areMultipleReadsAllowed() { + return multipleReadsAllowed; + } + boolean hasMappings() { return mappingFrom.size() > 0; } + protected LinearScan getAllocator() { + return allocator; + } + MoveResolver(LinearScan allocator) { this.allocator = allocator; @@ -80,18 +90,21 @@ this.insertIdx = -1; this.insertionBuffer = new LIRInsertionBuffer(); this.registerBlocked = new int[allocator.registers.length]; - assert checkEmpty(); } boolean checkEmpty() { assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing"; - for (int i = 0; i < allocator.registers.length; i++) { + for (int i = 0; i < getAllocator().registers.length; i++) { assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; } - assert !multipleReadsAllowed : "must have default value"; + checkMultipleReads(); return true; } + protected void checkMultipleReads() { + assert !areMultipleReadsAllowed() : "must have default value"; + } + private boolean verifyBeforeResolve() { assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal"; assert mappingFrom.size() == mappingTo.size() : "length must be equal"; @@ -99,7 +112,7 @@ int i; int j; - if (!multipleReadsAllowed) { + if (!areMultipleReadsAllowed()) { for (i = 0; i < mappingFrom.size(); i++) { for (j = i + 1; j < mappingFrom.size(); j++) { assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; @@ -114,7 +127,7 @@ } HashSet usedRegs = new HashSet<>(); - if (!multipleReadsAllowed) { + if (!areMultipleReadsAllowed()) { for (i = 0; i < mappingFrom.size(); i++) { Interval interval = mappingFrom.get(i); if (interval != null && !isIllegal(interval.location())) { @@ -136,37 +149,43 @@ assert unique : "cannot write to same register twice"; } - usedRegs.clear(); - for (i = 0; i < mappingFrom.size(); i++) { + verifyStackSlotMapping(); + + return true; + } + + protected void verifyStackSlotMapping() { + HashSet usedRegs = new HashSet<>(); + for (int i = 0; i < mappingFrom.size(); i++) { Interval interval = mappingFrom.get(i); if (interval != null && !isRegister(interval.location())) { usedRegs.add(interval.location()); } } - for (i = 0; i < mappingTo.size(); i++) { + for (int i = 0; i < mappingTo.size(); i++) { Interval interval = mappingTo.get(i); assert !usedRegs.contains(interval.location()) || interval.location().equals(mappingFrom.get(i).location()) : "stack slots used in mappingFrom must be disjoint to mappingTo"; } - - return true; } // mark assignedReg and assignedRegHi of the interval as blocked private void blockRegisters(Interval interval) { Value location = interval.location(); - if (isRegister(location)) { - assert multipleReadsAllowed || valueBlocked(location) == 0 : "register already marked as used"; + if (mightBeBlocked(location)) { + assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; int direction = 1; setValueBlocked(location, direction); + Debug.log("block %s", location); } } // mark assignedReg and assignedRegHi of the interval as unblocked private void unblockRegisters(Interval interval) { Value location = interval.location(); - if (isRegister(location)) { - assert valueBlocked(location) > 0 : "register already marked as unused"; + if (mightBeBlocked(location)) { + assert valueBlocked(location) > 0 : "location already marked as unused: " + location; setValueBlocked(location, -1); + Debug.log("unblock %s", location); } } @@ -177,9 +196,9 @@ private boolean safeToProcessMove(Interval from, Interval to) { Value fromReg = from != null ? from.location() : null; - Value reg = to.location(); - if (isRegister(reg)) { - if (valueBlocked(reg) > 1 || (valueBlocked(reg) == 1 && !reg.equals(fromReg))) { + Value location = to.location(); + if (mightBeBlocked(location)) { + if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !location.equals(fromReg)))) { return false; } } @@ -187,6 +206,10 @@ return true; } + protected boolean mightBeBlocked(Value location) { + return isRegister(location); + } + private void createInsertionBuffer(List list) { assert !insertionBuffer.initialized() : "overwriting existing buffer"; insertionBuffer.init(list); @@ -206,22 +229,30 @@ assert fromInterval.kind().equals(toInterval.kind()) : "move between different types"; assert insertIdx != -1 : "must setup insert position first"; - AllocatableValue fromOpr = fromInterval.operand; - AllocatableValue toOpr = toInterval.operand; - - insertionBuffer.append(insertIdx, allocator.getSpillMoveFactory().createMove(toOpr, fromOpr)); + insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); if (Debug.isLogEnabled()) { Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); } } + /** + * @param fromOpr {@link Interval#operand operand} of the {@code from} interval + * @param toOpr {@link Interval#operand operand} of the {@code to} interval + * @param fromLocation {@link Interval#location() location} of the {@code to} interval + * @param toLocation {@link Interval#location() location} of the {@code to} interval + */ + protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { + return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); + } + private void insertMove(Value fromOpr, Interval toInterval) { assert fromOpr.getLIRKind().equals(toInterval.kind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind()); assert insertIdx != -1 : "must setup insert position first"; AllocatableValue toOpr = toInterval.operand; - insertionBuffer.append(insertIdx, allocator.getSpillMoveFactory().createMove(toOpr, fromOpr)); + LIRInstruction move = getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); + insertionBuffer.append(insertIdx, move); if (Debug.isLogEnabled()) { Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); @@ -229,80 +260,86 @@ } private void resolveMappings() { - assert verifyBeforeResolve(); + try (Indent indent = Debug.logAndIndent("resolveMapping")) { + assert verifyBeforeResolve(); + if (Debug.isLogEnabled()) { + printMapping(); + } - // Block all registers that are used as input operands of a move. - // When a register is blocked, no move to this register is emitted. - // This is necessary for detecting cycles in moves. - int i; - for (i = mappingFrom.size() - 1; i >= 0; i--) { - Interval fromInterval = mappingFrom.get(i); - if (fromInterval != null) { - blockRegisters(fromInterval); - } - } - - int spillCandidate = -1; - while (mappingFrom.size() > 0) { - boolean processedInterval = false; - + // Block all registers that are used as input operands of a move. + // When a register is blocked, no move to this register is emitted. + // This is necessary for detecting cycles in moves. + int i; for (i = mappingFrom.size() - 1; i >= 0; i--) { Interval fromInterval = mappingFrom.get(i); - Interval toInterval = mappingTo.get(i); - - if (safeToProcessMove(fromInterval, toInterval)) { - // this interval can be processed because target is free - if (fromInterval != null) { - insertMove(fromInterval, toInterval); - unblockRegisters(fromInterval); - } else { - insertMove(mappingFromOpr.get(i), toInterval); - } - mappingFrom.remove(i); - mappingFromOpr.remove(i); - mappingTo.remove(i); - - processedInterval = true; - } else if (fromInterval != null && isRegister(fromInterval.location())) { - // this interval cannot be processed now because target is not free - // it starts in a register, so it is a possible candidate for spilling - spillCandidate = i; + if (fromInterval != null) { + blockRegisters(fromInterval); } } - if (!processedInterval) { - // no move could be processed because there is a cycle in the move list - // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory - assert spillCandidate != -1 : "no interval in register for spilling found"; + int spillCandidate = -1; + while (mappingFrom.size() > 0) { + boolean processedInterval = false; - // create a new spill interval and assign a stack slot to it - Interval fromInterval = mappingFrom.get(spillCandidate); - Interval spillInterval = allocator.createDerivedInterval(fromInterval); - spillInterval.setKind(fromInterval.kind()); + for (i = mappingFrom.size() - 1; i >= 0; i--) { + Interval fromInterval = mappingFrom.get(i); + Interval toInterval = mappingTo.get(i); - // add a dummy range because real position is difficult to calculate - // Note: this range is a special case when the integrity of the allocation is - // checked - spillInterval.addRange(1, 2); + if (safeToProcessMove(fromInterval, toInterval)) { + // this interval can be processed because target is free + if (fromInterval != null) { + insertMove(fromInterval, toInterval); + unblockRegisters(fromInterval); + } else { + insertMove(mappingFromOpr.get(i), toInterval); + } + mappingFrom.remove(i); + mappingFromOpr.remove(i); + mappingTo.remove(i); - // do not allocate a new spill slot for temporary interval, but - // use spill slot assigned to fromInterval. Otherwise moves from - // one stack slot to another can happen (not allowed by LIRAssembler - StackSlotValue spillSlot = fromInterval.spillSlot(); - if (spillSlot == null) { - spillSlot = allocator.frameMapBuilder.allocateSpillSlot(spillInterval.kind()); - fromInterval.setSpillSlot(spillSlot); - } - spillInterval.assignLocation(spillSlot); - - if (Debug.isLogEnabled()) { - Debug.log("created new Interval for spilling: %s", spillInterval); + processedInterval = true; + } else if (fromInterval != null && isRegister(fromInterval.location())) { + // this interval cannot be processed now because target is not free + // it starts in a register, so it is a possible candidate for spilling + spillCandidate = i; + } } - // insert a move from register to stack and update the mapping - insertMove(fromInterval, spillInterval); - mappingFrom.set(spillCandidate, spillInterval); - unblockRegisters(fromInterval); + if (!processedInterval) { + // no move could be processed because there is a cycle in the move list + // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory + assert spillCandidate != -1 : "no interval in register for spilling found"; + + // create a new spill interval and assign a stack slot to it + Interval fromInterval = mappingFrom.get(spillCandidate); + Interval spillInterval = getAllocator().createDerivedInterval(fromInterval); + spillInterval.setKind(fromInterval.kind()); + + // add a dummy range because real position is difficult to calculate + // Note: this range is a special case when the integrity of the allocation is + // checked + spillInterval.addRange(1, 2); + + // do not allocate a new spill slot for temporary interval, but + // use spill slot assigned to fromInterval. Otherwise moves from + // one stack slot to another can happen (not allowed by LIRAssembler + StackSlotValue spillSlot = fromInterval.spillSlot(); + if (spillSlot == null) { + spillSlot = getAllocator().frameMapBuilder.allocateSpillSlot(spillInterval.kind()); + fromInterval.setSpillSlot(spillSlot); + } + spillInterval.assignLocation(spillSlot); + + if (Debug.isLogEnabled()) { + Debug.log("created new Interval for spilling: %s", spillInterval); + } + blockRegisters(spillInterval); + + // insert a move from register to stack and update the mapping + insertMove(fromInterval, spillInterval); + mappingFrom.set(spillCandidate, spillInterval); + unblockRegisters(fromInterval); + } } } @@ -313,6 +350,23 @@ assert checkEmpty(); } + private void printMapping() { + try (Indent indent = Debug.logAndIndent("Mapping")) { + for (int i = mappingFrom.size() - 1; i >= 0; i--) { + Interval fromInterval = mappingFrom.get(i); + Interval toInterval = mappingTo.get(i); + Value from; + Value to = toInterval.location(); + if (fromInterval == null) { + from = mappingFromOpr.get(i); + } else { + from = fromInterval.location(); + } + Debug.log("move %s <- %s", from, to); + } + } + } + void setInsertPosition(List insertList, int insertIdx) { assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.lir.LIRValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.alloc.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; +import com.oracle.graal.lir.ssa.*; +import com.oracle.graal.lir.ssa.SSAUtils.PhiValueVisitor; + +final class SSALinearScan extends LinearScan { + + private static final DebugMetric numPhiResolutionMoves = Debug.metric("SSA LSRA[numPhiResolutionMoves]"); + private static final DebugMetric numStackToStackMoves = Debug.metric("SSA LSRA[numStackToStackMoves]"); + + SSALinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) { + super(target, res, spillMoveFactory, regAllocConfig); + } + + @Override + protected MoveResolver createMoveResolver() { + SSAMoveResolver moveResolver = new SSAMoveResolver(this); + assert moveResolver.checkEmpty(); + return moveResolver; + } + + @Override + protected int firstInstructionOfInterest() { + // also look at Labels as they define PHI values + return 0; + } + + @Override + void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { + super.resolveCollectMappings(fromBlock, toBlock, midBlock, moveResolver); + + if (toBlock.getPredecessorCount() > 1) { + int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock); + int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1; + + AbstractBlockBase phiOutBlock = midBlock != null ? midBlock : fromBlock; + List instructions = ir.getLIRforBlock(phiOutBlock); + int phiOutIdx = SSAUtils.phiOutIndex(ir, phiOutBlock); + int phiOutId = midBlock != null ? fromBlockLastInstructionId : instructions.get(phiOutIdx).id(); + assert phiOutId >= 0; + + PhiValueVisitor visitor = new PhiValueVisitor() { + + public void visit(Value phiIn, Value phiOut) { + Interval toInterval = splitChildAtOpId(intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); + if (isConstant(phiOut)) { + numPhiResolutionMoves.increment(); + moveResolver.addMapping(phiOut, toInterval); + } else { + Interval fromInterval = splitChildAtOpId(intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF); + if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { + numPhiResolutionMoves.increment(); + if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) { + moveResolver.addMapping(fromInterval, toInterval); + } else { + numStackToStackMoves.increment(); + moveResolver.addMapping(fromInterval, toInterval); + } + } + } + } + }; + + SSAUtils.forEachPhiValuePair(ir, toBlock, phiOutBlock, visitor); + SSAUtils.removePhiOut(ir, phiOutBlock); + } + } + + @Override + protected void beforeSpillMoveElimination() { + /* + * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value + * matches (ie. there is no resolution move) are falsely detected as errors. + */ + try (Scope s1 = Debug.scope("Remove Phi In")) { + for (AbstractBlockBase toBlock : sortedBlocks) { + if (toBlock.getPredecessorCount() > 1) { + SSAUtils.removePhiIn(ir, toBlock); + } + } + } + } + + @Override + protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { + // SSA Linear Scan might introduce moves to stack slots + assert isVariable(move.getResult()) || LinearScanPhase.SSA_LSRA.getValue() : "Move should not be produced in a non-SSA compilation: " + move; + + Interval curInterval = intervalFor(move.getResult()); + + if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory() && !isPhiResolutionMove(block, move, curInterval)) { + assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); + return true; + } + return false; + } + + @Override + void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { + super.addRegisterHint(op, targetValue, mode, flags, hintAtDef); + + if (hintAtDef && op instanceof LabelOp) { + LabelOp label = (LabelOp) op; + + Interval to = getOrCreateInterval((AllocatableValue) targetValue); + + SSAUtils.forEachPhiRegisterHint(ir, blockForId(label.id()), label, targetValue, mode, (ValueConsumer) (registerHint, valueMode, valueFlags) -> { + if (isVariableOrRegister(registerHint)) { + Interval from = getOrCreateInterval((AllocatableValue) registerHint); + + setHint(op, to, from); + setHint(op, from, to); + } + }); + } + } + + private static void setHint(final LIRInstruction op, Interval target, Interval source) { + Interval currentHint = target.locationHint(false); + if (currentHint == null || currentHint.from() > target.from()) { + /* + * Update hint if there was none or if the hint interval starts after the hinted + * interval. + */ + target.setLocationHint(source); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber); + } + } + } + + private boolean isPhiResolutionMove(AbstractBlockBase block, MoveOp move, Interval toInterval) { + if (!LinearScanPhase.SSA_LSRA.getValue()) { + return false; + } + if (!toInterval.isSplitParent()) { + return false; + } + if ((toInterval.from() & 1) == 1) { + // phi intervals start at even positions. + return false; + } + if (block.getSuccessorCount() != 1) { + return false; + } + LIRInstruction op = instructionForId(toInterval.from()); + if (!(op instanceof LabelOp)) { + return false; + } + AbstractBlockBase intStartBlock = blockForId(toInterval.from()); + assert ir.getLIRforBlock(intStartBlock).get(0).equals(op); + if (!block.getSuccessors().get(0).equals(intStartBlock)) { + return false; + } + try (Indent indet = Debug.indent()) { + Debug.log("Is a move (%s) to phi interval %s", move, toInterval); + } + return true; + } +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSAMoveResolver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSAMoveResolver.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.framemap.*; + +final class SSAMoveResolver extends MoveResolver { + + private int[] stackBlocked; + + SSAMoveResolver(LinearScan allocator) { + super(allocator); + this.stackBlocked = new int[((FrameMapBuilderTool) allocator.frameMapBuilder).getNumberOfStackSlots()]; + } + + @Override + boolean checkEmpty() { + for (int i = 0; i < stackBlocked.length; i++) { + assert stackBlocked[i] == 0 : "stack map must be empty before and after processing"; + } + return super.checkEmpty(); + } + + @Override + protected void checkMultipleReads() { + // multiple reads are allowed in SSA LSRA + } + + @Override + protected void verifyStackSlotMapping() { + // relax disjoint stack maps invariant + } + + @Override + protected boolean areMultipleReadsAllowed() { + return true; + } + + @Override + protected boolean mightBeBlocked(Value location) { + if (super.mightBeBlocked(location)) { + return true; + } + if (isStackSlotValue(location)) { + return true; + } + return false; + } + + @Override + protected void setValueBlocked(Value location, int direction) { + assert direction == 1 || direction == -1 : "out of bounds"; + if (isVirtualStackSlot(location)) { + assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!"; + int stack = asVirtualStackSlot(location).getId(); + if (stack >= stackBlocked.length) { + stackBlocked = Arrays.copyOf(stackBlocked, stack + 1); + } + stackBlocked[stack] += direction; + } else if (isStackSlot(location)) { + assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!"; + assert asStackSlot(location).isInCallerFrame() : "Unexpected stack slot: " + location; + // incoming stack arguments can be ignored + } else { + super.setValueBlocked(location, direction); + } + } + + @Override + protected int valueBlocked(Value location) { + if (isVirtualStackSlot(location)) { + assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!"; + int stack = asVirtualStackSlot(location).getId(); + if (stack >= stackBlocked.length) { + return 0; + } + return stackBlocked[stack]; + } + if (isStackSlot(location)) { + assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!"; + assert asStackSlot(location).isInCallerFrame() : "Unexpected stack slot: " + location; + // incoming stack arguments are always blocked (aka they can not be written) + return 1; + } + return super.valueBlocked(location); + } + + @Override + protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { + if (isStackSlotValue(toLocation) && isStackSlotValue(fromLocation)) { + return getAllocator().getSpillMoveFactory().createStackMove(toOpr, fromOpr); + } + return super.createMove(fromOpr, toOpr, fromLocation, toLocation); + } +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationStage.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationStage.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationStage.java Wed May 06 17:14:04 2015 +0200 @@ -22,11 +22,17 @@ */ package com.oracle.graal.lir.phases; +import static com.oracle.graal.compiler.common.GraalOptions.*; + import com.oracle.graal.lir.constopt.*; -import com.oracle.graal.lir.phases.PreAllocationOptimizationPhase.*; +import com.oracle.graal.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext; +import com.oracle.graal.lir.ssa.*; public class PreAllocationOptimizationStage extends LIRPhaseSuite { public PreAllocationOptimizationStage() { + if (SSA_LIR.getValue() && SSADestructionPhase.Options.LIREagerSSADestruction.getValue()) { + appendPhase(new SSADestructionPhase()); + } if (ConstantLoadOptimization.Options.LIROptConstantLoadOptimization.getValue()) { appendPhase(new ConstantLoadOptimization()); } diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSADestructionPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSADestructionPhase.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.ssa; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.phases.*; +import com.oracle.graal.options.*; + +public final class SSADestructionPhase extends PreAllocationOptimizationPhase { + + public static class Options { + // @formatter:off + @Option(help = "Destruct SSA LIR eagerly (before other LIR phases).", type = OptionType.Debug) + public static final OptionValue LIREagerSSADestruction = new OptionValue<>(false); + // @formatter:on + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, LIRGeneratorTool lirGen) { + LIR lir = lirGenRes.getLIR(); + AbstractControlFlowGraph cfg = lir.getControlFlowGraph(); + for (AbstractBlockBase block : cfg.getBlocks()) { + doBlock(block, lir, lirGen); + } + } + + private static void doBlock(AbstractBlockBase block, LIR lir, LIRGeneratorTool lirGen) { + if (block.getPredecessorCount() > 1) { + for (AbstractBlockBase pred : block.getPredecessors()) { + + List instructions = lir.getLIRforBlock(pred); + + int insertBefore = SSAUtils.phiOutIndex(lir, pred); + + PhiResolver resolver = PhiResolver.create(lirGen, new LIRInsertionBuffer(), instructions, insertBefore); + SSAUtils.forEachPhiValuePair(lir, block, pred, resolver::move); + resolver.dispose(); + + SSAUtils.removePhiOut(lir, pred); + } + SSAUtils.removePhiIn(lir, block); + } + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAUtils.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.ssa; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.StandardOp.JumpOp; +import com.oracle.graal.lir.StandardOp.LabelOp; + +/** + * Utilities for working with Static-Single-Assignment LIR form. + * + *

Representation of PHIs

+ * + * There is no explicit PHI {@linkplain LIRInstruction}. Instead, they are implemented + * as parallel copy that span across a control-flow edge. + * + * The variables introduced by PHIs of a specific {@linkplain AbstractBlockBase merge + * block} are {@linkplain LabelOp#setIncomingValues attached} to the {@linkplain LabelOp} of the + * block. The outgoing values from the predecessor are {@link JumpOp#setOutgoingValues input} to the + * {@linkplain BlockEndOp} of the predecessor. Because there are no critical edges we know that the + * {@link BlockEndOp} of the predecessor has to be a {@link JumpOp}. + * + *

Example:

+ * + *
+ * B0 -> B1
+ *   ...
+ *   v0|i = ...
+ *   JUMP ~[v0|i, int[0|0x0]] destination: B0 -> B1
+ * ________________________________________________
+ * 
+ * B2 -> B1
+ *   ...
+ *   v1|i = ...
+ *   v2|i = ...
+ *   JUMP ~[v1|i, v2|i] destination: B2 -> B1
+ * ________________________________________________
+ * 
+ * B1 <- B0,B2
+ *   [v3|i, v4|i] = LABEL
+ *   ...
+ * 
+ */ +public final class SSAUtils { + + public interface PhiValueVisitor { + /** + * @param phiIn the incoming value at the merge block + * @param phiOut the outgoing value from the predecessor block + */ + void visit(Value phiIn, Value phiOut); + } + + /** + * Visits each phi value pair of an edge, i.e. the outgoing value from the predecessor and the + * incoming value to the merge block. + */ + public static void forEachPhiValuePair(LIR lir, AbstractBlockBase merge, AbstractBlockBase pred, PhiValueVisitor visitor) { + if (merge.getPredecessorCount() < 2) { + return; + } + assert merge.getPredecessors().contains(pred) : String.format("%s not in predecessor list: %s", pred, merge.getPredecessors()); + assert pred.getSuccessorCount() == 1 : String.format("Merge predecessor block %s has more than one successor? %s", pred, pred.getSuccessors()); + assert pred.getSuccessors().get(0) == merge : String.format("Predecessor block %s has wrong successor: %s, should be: %s", pred, pred.getSuccessors().get(0), merge); + + JumpOp jump = phiOut(lir, pred); + LabelOp label = phiIn(lir, merge); + + assert label.getIncomingSize() == jump.getOutgoingSize() : String.format("Phi In/Out size mismatch: in=%d vs. out=%d", label.getIncomingSize(), jump.getOutgoingSize()); + + for (int i = 0; i < label.getIncomingSize(); i++) { + visitor.visit(label.getIncomingValue(i), jump.getOutgoingValue(i)); + } + } + + private static JumpOp phiOut(LIR lir, AbstractBlockBase block) { + assert block.getSuccessorCount() == 1; + List instructions = lir.getLIRforBlock(block); + int index = instructions.size() - 1; + LIRInstruction op = instructions.get(index); + return (JumpOp) op; + } + + public static int phiOutIndex(LIR lir, AbstractBlockBase block) { + assert block.getSuccessorCount() == 1; + List instructions = lir.getLIRforBlock(block); + int index = instructions.size() - 1; + assert instructions.get(index) instanceof JumpOp; + return index; + } + + private static LabelOp phiIn(LIR lir, AbstractBlockBase block) { + assert block.getPredecessorCount() > 1; + LabelOp label = (LabelOp) lir.getLIRforBlock(block).get(0); + return label; + } + + public static void removePhiOut(LIR lir, AbstractBlockBase block) { + JumpOp jump = phiOut(lir, block); + jump.clearOutgoingValues(); + } + + public static void removePhiIn(LIR lir, AbstractBlockBase block) { + LabelOp label = phiIn(lir, block); + label.clearIncomingValues(); + } + + public static boolean verifySSAForm(LIR lir) { + return new SSAVerifier(lir).verify(); + } + + public static void verifyPhi(LIR lir, AbstractBlockBase merge) { + assert merge.getPredecessorCount() > 1; + for (AbstractBlockBase pred : merge.getPredecessors()) { + forEachPhiValuePair(lir, merge, pred, (phiIn, phiOut) -> { + assert phiIn.getLIRKind().equals(phiOut.getLIRKind()) || + (phiIn.getPlatformKind().equals(phiOut.getPlatformKind()) && phiIn.getLIRKind().isDerivedReference() && phiOut.getLIRKind().isValue()); + }); + } + } + + public static void forEachPhiRegisterHint(LIR lir, AbstractBlockBase block, LabelOp label, Value targetValue, OperandMode mode, ValueConsumer valueConsumer) { + assert mode == OperandMode.DEF : "Wrong operand mode: " + mode; + assert lir.getLIRforBlock(block).get(0).equals(label) : String.format("Block %s and Label %s do not match!", block, label); + + if (!label.isPhiIn()) { + return; + } + int idx = indexOfValue(label, targetValue); + assert idx >= 0 : String.format("Value %s not in label %s", targetValue, label); + + for (AbstractBlockBase pred : block.getPredecessors()) { + JumpOp jump = phiOut(lir, pred); + Value sourceValue = jump.getOutgoingValue(idx); + valueConsumer.visitValue(jump, sourceValue, null, null); + } + + } + + private static int indexOfValue(LabelOp label, Value value) { + for (int i = 0; i < label.getIncomingSize(); i++) { + if (label.getIncomingValue(i).equals(value)) { + return i; + } + } + return -1; + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAVerifier.java Wed May 06 17:14:04 2015 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.lir.ssa; + +import static com.oracle.graal.api.code.ValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; + +final class SSAVerifier { + private static class Entry { + private final LIRInstruction inst; + private final AbstractBlockBase block; + + Entry(LIRInstruction inst, AbstractBlockBase block) { + this.inst = inst; + this.block = block; + } + } + + private final LIR lir; + private final BitSet visited; + private final HashMap defined; + private AbstractBlockBase currentBlock; + + SSAVerifier(LIR lir) { + this.lir = lir; + this.visited = new BitSet(lir.getControlFlowGraph().getBlocks().size()); + this.defined = new HashMap<>(); + } + + public boolean verify() { + try (Scope s = Debug.scope("SSAVerifier", lir)) { + for (AbstractBlockBase block : lir.getControlFlowGraph().getBlocks()) { + doBlock(block); + } + } catch (Throwable e) { + throw Debug.handle(e); + } + return true; + } + + private void doBlock(AbstractBlockBase b) { + if (visited.get(b.getId())) { + return; + } + for (AbstractBlockBase pred : b.getPredecessors()) { + if (!b.isLoopHeader() || !pred.isLoopEnd()) { + doBlock(pred); + } + } + try (Indent indent = Debug.logAndIndent("handle block %s", b)) { + assert verifyBlock(b); + } + } + + private boolean verifyBlock(AbstractBlockBase block) { + currentBlock = block; + assert !visited.get(block.getId()) : "Block already visited: " + block; + visited.set(block.getId()); + for (LIRInstruction op : lir.getLIRforBlock(block)) { + op.visitEachAlive(this::useConsumer); + op.visitEachState(this::useConsumer); + op.visitEachInput(this::useConsumer); + + op.visitEachTemp(this::defConsumer); + op.visitEachOutput(this::defConsumer); + + } + currentBlock = null; + return true; + } + + /** + * @see InstructionValueConsumer + * @param mode + * @param flags + */ + private void useConsumer(LIRInstruction inst, Value value, OperandMode mode, EnumSet flags) { + if (shouldProcess(value)) { + assert defined.keySet().contains(value) || flags.contains(OperandFlag.UNINITIALIZED) : String.format("Value %s used at instruction %s in block %s but never defined", value, inst, + currentBlock); + } + } + + /** + * @see InstructionValueConsumer + * @param mode + * @param flags + */ + private void defConsumer(LIRInstruction inst, Value value, OperandMode mode, EnumSet flags) { + if (shouldProcess(value)) { + assert !defined.keySet().contains(value) : String.format("Value %s redefined at %s but never defined (previous definition %s in block %s)", value, inst, defined.get(value).inst, + defined.get(value).block); + defined.put(value, new Entry(inst, currentBlock)); + } + } + + private static boolean shouldProcess(Value value) { + return !value.equals(Value.ILLEGAL) && !isConstant(value) && !isRegister(value) && !isStackSlotValue(value); + } + +} diff -r ccddbb1409d2 -r 0927730ed87f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractMergeNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractMergeNode.java Wed May 06 17:13:50 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractMergeNode.java Wed May 06 17:14:04 2015 +0200 @@ -133,6 +133,10 @@ return this.usages().filter(PhiNode.class).filter(this::isPhiAtMerge); } + public NodeIterable valuePhis() { + return this.usages().filter(ValuePhiNode.class).filter(this::isPhiAtMerge); + } + @Override public NodeIterable anchored() { return super.anchored().filter(n -> !isPhiAtMerge(n)); diff -r ccddbb1409d2 -r 0927730ed87f mx/suite.py --- a/mx/suite.py Wed May 06 17:13:50 2015 +0200 +++ b/mx/suite.py Wed May 06 17:14:04 2015 +0200 @@ -548,6 +548,17 @@ "workingSets" : "Graal,LIR", }, + "com.oracle.graal.lir.jtt" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.graal.jtt", + ], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", + "workingSets" : "Graal,LIR", + }, + "com.oracle.graal.lir.test" : { "subDir" : "graal", "sourceDirs" : ["src"],