# HG changeset patch # User Lukas Stadler # Date 1382100716 -7200 # Node ID 36a438ebab50543b690034967614ee1536e4bdfe # Parent b4b7d39cdf73b58c727a9a485532c139a9ed77c7 duplicate VirtualObjectNodes when peeling / unrolling loops diff -r b4b7d39cdf73 -r 36a438ebab50 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Fri Oct 18 13:49:41 2013 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Fri Oct 18 14:51:56 2013 +0200 @@ -22,8 +22,6 @@ */ package com.oracle.graal.compiler.test.ea; -import java.util.concurrent.*; - import org.junit.*; import com.oracle.graal.api.code.*; @@ -32,12 +30,14 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.java.*; +import com.oracle.graal.loop.phases.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.schedule.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; @@ -223,31 +223,97 @@ testEscapeAnalysis("testNewNodeSnippet", null, false); } - private ReturnNode testEscapeAnalysis(String snippet, final Constant expectedConstantResult, final boolean iterativeEscapeAnalysis) { - ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(snippet)); - final StructuredGraph graph = new StructuredGraph(method); + private static final TestObject2 staticObj = new TestObject2(null); + + public static Object testFullyUnrolledLoopSnippet() { + /* + * This tests a case that can appear if PEA is performed both before and after loop + * unrolling/peeling: If the VirtualInstanceNode is not anchored correctly to stay within + * the loop, it will not be duplicated, and therefore the resulting object will reference + * itself, and not a second (different) object. + */ + TestObject2 obj = staticObj; + for (int i = 0; i < 2; i++) { + obj = new TestObject2(obj); + } + return obj.o; + } - return Debug.scope("GraalCompiler", new Object[]{graph, method, getCodeCache()}, new Callable() { + @Test + public void testFullyUnrolledLoop() { + testEscapeAnalysisUnrolled("testFullyUnrolledLoopSnippet"); + } + + private static Object staticField; + + private static TestObject2 inlinedPart(TestObject2 obj) { + TestObject2 ret = new TestObject2(obj); + staticField = null; + return ret; + } + + public static Object testPeeledLoopSnippet() { + TestObject2 obj = staticObj; + int i = 0; + do { + obj = inlinedPart(obj); + } while (i++ < 10); + staticField = obj; + return obj.o; + } - public ReturnNode call() { - new GraphBuilderPhase(getMetaAccess(), getForeignCalls(), GraphBuilderConfiguration.getEagerDefault(), OptimisticOptimizations.ALL).apply(graph); + @Test + public void testPeeledLoop() { + testEscapeAnalysisPeeled("testPeeledLoopSnippet"); + } + + private StructuredGraph graph; + private HighTierContext context; + private ReturnNode returnNode; + + private void testEscapeAnalysis(String snippet, final Constant expectedConstantResult, final boolean iterativeEscapeAnalysis) { + prepareGraph(snippet, iterativeEscapeAnalysis); + if (expectedConstantResult != null) { + Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant()); + Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant()); + } + int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() + + graph.getNodes().filter(CommitAllocationNode.class).count(); + Assert.assertEquals(0, newInstanceCount); + } + private void testEscapeAnalysisUnrolled(String snippet) { + prepareGraph(snippet, false); + new LoopFullUnrollPhase(new CanonicalizerPhase(true)).apply(graph, context); + new PartialEscapePhase(false, new CanonicalizerPhase(true)).apply(graph, context); + Assert.assertTrue(returnNode.result() instanceof AllocatedObjectNode); + CommitAllocationNode commit = ((AllocatedObjectNode) returnNode.result()).getCommit(); + Assert.assertEquals(1, commit.getValues().size()); + Assert.assertEquals(1, commit.getVirtualObjects().size()); + Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0)); + } + + private void testEscapeAnalysisPeeled(String snippet) { + prepareGraph(snippet, false); + new LoopTransformHighPhase().apply(graph); + new LoopTransformLowPhase().apply(graph); + new SchedulePhase().apply(graph); + } + + private void prepareGraph(String snippet, final boolean iterativeEscapeAnalysis) { + graph = new StructuredGraph(getMetaAccess().lookupJavaMethod(getMethod(snippet))); + Debug.scope("GraalCompiler", new Object[]{graph, getMetaAccess().lookupJavaMethod(getMethod(snippet)), getCodeCache()}, new Runnable() { + + public void run() { + new GraphBuilderPhase(getMetaAccess(), getForeignCalls(), GraphBuilderConfiguration.getEagerDefault(), OptimisticOptimizations.ALL).apply(graph); Assumptions assumptions = new Assumptions(false); - HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL); + context = new HighTierContext(getProviders(), assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL); new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context); new DeadCodeEliminationPhase().apply(graph); new CanonicalizerPhase(true).apply(graph, context); new PartialEscapePhase(iterativeEscapeAnalysis, new CanonicalizerPhase(true)).apply(graph, context); Assert.assertEquals(1, graph.getNodes().filter(ReturnNode.class).count()); - ReturnNode returnNode = graph.getNodes().filter(ReturnNode.class).first(); - if (expectedConstantResult != null) { - Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant()); - Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant()); - } - int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() + - graph.getNodes().filter(CommitAllocationNode.class).count(); - Assert.assertEquals(0, newInstanceCount); - return returnNode; + returnNode = graph.getNodes().filter(ReturnNode.class).first(); } }); } diff -r b4b7d39cdf73 -r 36a438ebab50 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java Fri Oct 18 13:49:41 2013 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java Fri Oct 18 14:51:56 2013 +0200 @@ -32,6 +32,7 @@ import com.oracle.graal.nodes.VirtualState.NodeClosure; import com.oracle.graal.nodes.VirtualState.VirtualClosure; import com.oracle.graal.nodes.cfg.*; +import com.oracle.graal.nodes.virtual.*; public abstract class LoopFragment { @@ -181,10 +182,16 @@ final NodeBitMap notloopNodes = graph.createNodeBitMap(true); for (AbstractBeginNode b : blocks) { for (Node n : b.getBlockNodes()) { + if (n instanceof CommitAllocationNode) { + for (VirtualObjectNode obj : ((CommitAllocationNode) n).getVirtualObjects()) { + markFloating(obj, nodes, notloopNodes); + } + } for (Node usage : n.usages()) { markFloating(usage, nodes, notloopNodes); } } + } return nodes;