# HG changeset patch # User Gilles Duboscq # Date 1347525917 -7200 # Node ID a718f153b9f25399343a41c567cd2b09bba17c09 # Parent 43e274f43a9ae247fcd173ddbb22fa46b5c73655# Parent ad97777056ec25fd197dce175405c8d64ac52788 Merge diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java Thu Sep 13 10:45:17 2012 +0200 @@ -32,6 +32,7 @@ import com.oracle.graal.compiler.schedule.*; import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; +import com.oracle.graal.graph.Node.*; import com.oracle.graal.lir.cfg.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; @@ -213,7 +214,8 @@ } } - public static void outputGraph(StructuredGraph graph) { + public static void outputGraph(StructuredGraph graph, String message) { + System.out.println("========================= " + message); SchedulePhase schedule = new SchedulePhase(); schedule.apply(graph); for (Block block : schedule.getCFG().getBlocks()) { @@ -227,7 +229,20 @@ } System.out.println(); for (Node node : schedule.getBlockToNodesMap().get(block)) { - System.out.println(" " + node + " (" + node.usages().size() + ")"); + outputNode(node); + } + } + } + + private static void outputNode(Node node) { + System.out.print(" " + node + " (usage count: " + node.usages().size() + ") (inputs:"); + for (Node input : node.inputs()) { + System.out.print(" " + input.toString(Verbosity.Id)); + } + System.out.println(")"); + if (node instanceof MergeNode) { + for (PhiNode phi : ((MergeNode) node).phis()) { + outputNode(phi); } } } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Thu Sep 13 10:45:17 2012 +0200 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.test.ea; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.*; +import com.oracle.graal.compiler.phases.*; +import com.oracle.graal.compiler.phases.ea.*; +import com.oracle.graal.compiler.test.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; + +/** + * In these test cases the probability of all invokes is set to a high value, such that an InliningPhase should inline them all. + * After that, the EscapeAnalysisPhase is expected to remove all allocations and return the correct values. + */ +public class EscapeAnalysisTest extends GraalCompilerTest { + + @Test + public void test1() { + test("test1Snippet", Constant.forInt(101)); + } + + @SuppressWarnings("all") + public static int test1Snippet(int a) { + Integer x = new Integer(101); + return x.intValue(); + } + + @Test + public void test2() { + test("test2Snippet", Constant.forInt(0)); + } + + @SuppressWarnings("all") + public static int test2Snippet(int a) { + Integer[] x = new Integer[0]; + return x.length; + } + + @Test + public void test3() { + test("test3Snippet", Constant.forObject(null)); + } + + @SuppressWarnings("all") + public static Object test3Snippet(int a) { + Integer[] x = new Integer[1]; + return x[0]; + } + + @Test + public void testMonitor() { + test("testMonitorSnippet", Constant.forInt(0)); + } + + private static native void notInlineable(); + + @SuppressWarnings("all") + public static int testMonitorSnippet(int a) { + Integer x = new Integer(0); + Integer[] y = new Integer[0]; + Integer[] z = new Integer[1]; + synchronized (x) { + synchronized (y) { + synchronized (z) { + notInlineable(); + } + } + } + return x.intValue(); + } + + @Test + public void testMonitor2() { + test("testMonitor2Snippet", Constant.forInt(0)); + } + + /** + * This test case differs from the last one in that it requires inlining within a synchronized region. + */ + @SuppressWarnings("all") + public static int testMonitor2Snippet(int a) { + Integer x = new Integer(0); + Integer[] y = new Integer[0]; + Integer[] z = new Integer[1]; + synchronized (x) { + synchronized (y) { + synchronized (z) { + notInlineable(); + return x.intValue(); + } + } + } + } + + @Test + public void testMerge() { + test("testMerge1Snippet", Constant.forInt(0)); + } + + public static class TestObject { + int x; + int y; + public TestObject(int x, int y) { + this.x = x; + this.y = y; + } + } + + public static int testMerge1Snippet(int a) { + TestObject obj = new TestObject(1, 0); + if (a < 0) { + obj.x = obj.x + 1; + } else { + obj.x = obj.x + 2; + obj.y = 0; + } + if (obj.x > 1000) { + return 1; + } + return obj.y; + } + + @Test + public void testSimpleLoop() { + test("testSimpleLoopSnippet", Constant.forInt(1)); + } + + public int testSimpleLoopSnippet(int a) { + TestObject obj = new TestObject(1, 2); + for (int i = 0; i < a; i++) { + notInlineable(); + } + return obj.x; + } + + private void test(final String snippet, final Constant expectedResult) { + Debug.scope("EscapeAnalysisTest", new DebugDumpScope(snippet), new Runnable() { + public void run() { + StructuredGraph graph = parse(snippet); + for (Invoke n : graph.getInvokes()) { + n.node().setProbability(100000); + } + + new InliningPhase(null, runtime(), null, null, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); + new DeadCodeEliminationPhase().apply(graph); + Debug.dump(graph, "Graph"); + new PartialEscapeAnalysisPhase(null, runtime(), null).apply(graph); + new CullFrameStatesPhase().apply(graph); + new CanonicalizerPhase(null, runtime(), null).apply(graph); + Debug.dump(graph, "Graph"); + int retCount = 0; + for (ReturnNode ret : graph.getNodes(ReturnNode.class)) { + Assert.assertTrue(ret.result().isConstant()); + Assert.assertEquals(ret.result().asConstant(), expectedResult); + retCount++; + } + Assert.assertEquals(1, retCount); + int newInstanceCount = 0; + for (@SuppressWarnings("unused") NewInstanceNode n : graph.getNodes(NewInstanceNode.class)) { + newInstanceCount++; + } + for (@SuppressWarnings("unused") NewObjectArrayNode n : graph.getNodes(NewObjectArrayNode.class)) { + newInstanceCount++; + } + Assert.assertEquals(0, newInstanceCount); + } + }); + } +} diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Thu Sep 13 10:45:17 2012 +0200 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.test.ea; + +import java.util.concurrent.*; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.oracle.graal.compiler.*; +import com.oracle.graal.compiler.phases.*; +import com.oracle.graal.compiler.phases.ea.*; +import com.oracle.graal.compiler.test.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; + +/** + * In these test cases the probability of all invokes is set to a high value, such that an InliningPhase should inline + * them all. After that, the PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct + * values. + */ +public class PartialEscapeAnalysisTest extends GraalCompilerTest { + + public static class TestObject { + + public int x; + public int y; + + public TestObject(int x, int y) { + this.x = x; + this.y = y; + } + } + + public static class TestObject2 { + + public Object x; + public Object y; + + public TestObject2(Object x, Object y) { + this.x = x; + this.y = y; + } + } + + @Test + public void test1() { + testMaterialize("test1Snippet", 0.25, 1); + } + + @SuppressWarnings("all") + public static Object test1Snippet(int a, int b) { + TestObject obj = new TestObject(1, 2); + if (a < 0) { + if (b < 0) { + return obj; + } else { + return obj.y; + } + } else { + return obj.x; + } + } + + @Test + public void test2() { + testMaterialize("test2Snippet", 1.5, 3, LoadIndexedNode.class); + } + + public static Object test2Snippet(int a) { + TestObject2 obj = new TestObject2(1, 2); + obj.x = new TestObject2(obj, 3); + if (a < 0) { + ((TestObject2) obj.x).y = null; + obj.y = null; + return obj; + } else { + ((TestObject2) obj.x).y = null; + ((TestObject2) obj.x).x = null; + return obj.x; + } + } + + @Test + public void test3() { + testMaterialize("test3Snippet", 0.5, 1, StoreFieldNode.class, LoadFieldNode.class); + } + + public static Object test3Snippet(int a) { + if (a < 0) { + TestObject obj = new TestObject(1, 2); + obj.x = 123; + obj.y = 234; + obj.x = 123111; + obj.y = new Integer(123).intValue(); + return obj; + } else { + return null; + } + } + + @SafeVarargs + final void testMaterialize(final String snippet, double expectedProbability, int expectedCount, Class... invalidNodeClasses) { + StructuredGraph result = processMethod(snippet); + Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", result.getNodes(NewInstanceNode.class).isEmpty()); + Assert.assertTrue("partial escape analysis should have removed all NewObjectArrayNode allocations", result.getNodes(NewObjectArrayNode.class).isEmpty()); + Assert.assertTrue("partial escape analysis should have removed all NewPrimitiveArrayNode allocations", result.getNodes(NewPrimitiveArrayNode.class).isEmpty()); + double probabilitySum = 0; + int materializeCount = 0; + for (MaterializeObjectNode materialize : result.getNodes(MaterializeObjectNode.class)) { + probabilitySum += materialize.probability(); + materializeCount++; + } + Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount); + Assert.assertEquals("unexpected probability of MaterializeObjectNodes", expectedProbability, probabilitySum, 0.01); + for (Node node : result.getNodes()) { + for (Class clazz : invalidNodeClasses) { + Assert.assertFalse("instance of invalid class: " + clazz.getSimpleName(), clazz.isInstance(node) && node.usages().isNotEmpty()); + } + } + } + + private StructuredGraph processMethod(final String snippet) { + return Debug.scope(getClass().getSimpleName(), new Callable() { + @Override + public StructuredGraph call() throws Exception { + StructuredGraph graph = parse(snippet); + new ComputeProbabilityPhase().apply(graph); + for (Invoke n : graph.getInvokes()) { + n.node().setProbability(100000); + } + new InliningPhase(null, runtime(), null, null, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new CanonicalizerPhase(null, runtime(), null).apply(graph); +// TypeSystemTest.outputGraph(graph, "before EscapeAnalysis " + snippet); + new PartialEscapeAnalysisPhase(null, runtime(), null).apply(graph); +// TypeSystemTest.outputGraph(graph, "after EscapeAnalysis " + snippet); + new CullFrameStatesPhase().apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new CanonicalizerPhase(null, runtime(), null).apply(graph); +// TypeSystemTest.outputGraph(graph, "after CullFrameStates " + snippet); + return graph; + } + }); + } +} diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Thu Sep 13 10:45:17 2012 +0200 @@ -33,6 +33,7 @@ import com.oracle.graal.compiler.gen.*; import com.oracle.graal.compiler.phases.*; import com.oracle.graal.compiler.phases.PhasePlan.PhasePosition; +import com.oracle.graal.compiler.phases.ea.*; import com.oracle.graal.compiler.schedule.*; import com.oracle.graal.compiler.target.*; import com.oracle.graal.debug.*; @@ -155,6 +156,9 @@ if (GraalOptions.EscapeAnalysis && !plan.isPhaseDisabled(EscapeAnalysisPhase.class)) { new EscapeAnalysisPhase(target, runtime, assumptions).apply(graph); } + if (GraalOptions.PartialEscapeAnalysis && !plan.isPhaseDisabled(PartialEscapeAnalysisPhase.class)) { + new PartialEscapeAnalysisPhase(target, runtime, assumptions).apply(graph); + } if (GraalOptions.OptLoopTransform) { new LoopTransformHighPhase().apply(graph); } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Thu Sep 13 10:45:17 2012 +0200 @@ -69,9 +69,8 @@ public static float ProbabilityCapForInlining = 1f; // escape analysis settings - public static boolean EscapeAnalysis = true; - public static int ForcedInlineEscapeWeight = 10; - public static boolean PrintEscapeAnalysis = ____; + public static boolean EscapeAnalysis = ____; + public static boolean PartialEscapeAnalysis = true; public static double TailDuplicationProbability = 0.5; public static int TailDuplicationTrivialSize = 1; diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/DeadCodeEliminationPhase.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/DeadCodeEliminationPhase.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/DeadCodeEliminationPhase.java Thu Sep 13 10:45:17 2012 +0200 @@ -29,6 +29,9 @@ public class DeadCodeEliminationPhase extends Phase { + // Metrics + private static final DebugMetric metricNodesRemoved = Debug.metric("NodesRemoved"); + private NodeFlood flood; @Override @@ -101,6 +104,7 @@ } for (Node node : graph.getNodes()) { if (!flood.isMarked(node)) { + metricNodesRemoved.increment(); node.safeDelete(); } } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/ea/PartialEscapeAnalysisPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/ea/PartialEscapeAnalysisPhase.java Thu Sep 13 10:45:17 2012 +0200 @@ -0,0 +1,1128 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.phases.ea; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaType.*; +import com.oracle.graal.compiler.*; +import com.oracle.graal.compiler.phases.*; +import com.oracle.graal.compiler.schedule.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.cfg.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.PhiNode.PhiType; +import com.oracle.graal.nodes.VirtualState.NodeClosure; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.virtual.*; + +class EscapeRecord { + + public final ResolvedJavaType type; + public final EscapeField[] fields; + public final HashMap fieldMap = new HashMap<>(); + public final VirtualObjectNode virtualObject; + + public EscapeRecord(ResolvedJavaType type, EscapeField[] fields, VirtualObjectNode virtualObject) { + this.type = type; + this.fields = fields; + this.virtualObject = virtualObject; + for (int i = 0; i < fields.length; i++) { + fieldMap.put(fields[i].representation(), i); + } + } + + @Override + public String toString() { + return MetaUtil.toJavaName(type, false) + "@" + (System.identityHashCode(this) % 10000); + } +} + +class EscapeAnalysisIteration { + + // Metrics + private static final DebugMetric metricAllocationRemoved = Debug.metric("AllocationRemoved"); + private static final DebugMetric metricAllocationFieldsRemoved = Debug.metric("AllocationFieldsRemoved"); + private static final DebugMetric metricStoreRemoved = Debug.metric("StoreRemoved"); + private static final DebugMetric metricLoadRemoved = Debug.metric("LoadRemoved"); + private static final DebugMetric metricLockRemoved = Debug.metric("LockRemoved"); + private static final DebugMetric metricOtherRemoved = Debug.metric("OtherRemoved"); + private static final DebugMetric metricMaterializations = Debug.metric("Materializations"); + private static final DebugMetric metricMaterializationFields = Debug.metric("MaterializationFields"); + + private static final ValueNode DUMMY_NODE = new ValueNode(null) { + }; + + public static final void trace(String format, Object... obj) { + if (GraalOptions.TraceEscapeAnalysis) { + Debug.log(format, obj); + } + } + + public static final void error(String format, Object... obj) { + System.out.print(String.format(format, obj)); + } + + private final StructuredGraph graph; + private final MetaAccessProvider runtime; + private final SchedulePhase schedule; + private final NodeBitMap usages; + private final NodeBitMap visitedNodes; + private boolean changed = false; + + private final boolean changeGraph; + + private final HashSet allocations; + private final ArrayList obsoleteNodes = new ArrayList<>(); + private int virtualIds = 0; + + public EscapeAnalysisIteration(StructuredGraph graph, SchedulePhase schedule, MetaAccessProvider runtime, HashSet allocations, boolean changeGraph) { + this.graph = graph; + this.schedule = schedule; + this.runtime = runtime; + this.allocations = allocations; + this.changeGraph = changeGraph; + this.visitedNodes = graph.createNodeBitMap(); + this.usages = graph.createNodeBitMap(); + } + + public void run() { + new PartialEscapeIterator(graph, schedule.getCFG().getStartBlock()).apply(); + + if (changeGraph) { + Debug.dump(graph, "after PartialEscapeAnalysis"); + + for (ValueNode node : obsoleteNodes) { + if (node.isAlive() && node instanceof FixedWithNextNode) { + FixedWithNextNode x = (FixedWithNextNode) node; + FixedNode next = x.next(); + x.setNext(null); + ((FixedWithNextNode) node.predecessor()).setNext(next); + } + } + new DeadCodeEliminationPhase().apply(graph); + + if (changed) { + Debug.log("escape analysis on %s\n", graph.method()); + } + + if (GraalOptions.TraceEscapeAnalysis) { + for (Node node : graph.getNodes()) { + if (!visitedNodes.isMarked(node) && !(node instanceof VirtualState) && !(node instanceof VirtualObjectNode)) { + trace("unvisited node: %s", node); + } + } + } + } + } + + private static class ObjectState { + + public final EscapeRecord record; + public ValueNode[] fieldState; + public ValueNode materializedValue; + public int lockCount; + public boolean initialized; + + public ObjectState(EscapeRecord record, ValueNode[] fieldState, int lockCount) { + this.record = record; + this.fieldState = fieldState; + this.lockCount = lockCount; + this.initialized = false; + } + + public ObjectState(EscapeRecord record, ValueNode materializedValue, int lockCount) { + this.record = record; + this.materializedValue = materializedValue; + this.lockCount = lockCount; + this.initialized = true; + } + + private ObjectState(ObjectState other) { + record = other.record; + fieldState = other.fieldState == null ? null : other.fieldState.clone(); + materializedValue = other.materializedValue; + lockCount = other.lockCount; + initialized = other.initialized; + } + + @Override + public ObjectState clone() { + return new ObjectState(this); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder().append('{'); + if (lockCount > 0) { + str.append('l').append(lockCount).append(' '); + } + if (fieldState != null) { + for (int i = 0; i < fieldState.length; i++) { + str.append(record.fields[i].name()).append('=').append(fieldState[i]).append(' '); + } + } + if (materializedValue != null) { + str.append("mat=").append(materializedValue); + } + + return str.append('}').toString(); + } + } + + private class BlockState implements MergeableBlockState { + + private final HashMap recordStates = new HashMap<>(); + private final HashMap recordAliases = new HashMap<>(); + + public BlockState() { + } + + public BlockState(BlockState other) { + for (Map.Entry entry : other.recordStates.entrySet()) { + recordStates.put(entry.getKey(), entry.getValue().clone()); + } + for (Map.Entry entry : other.recordAliases.entrySet()) { + recordAliases.put(entry.getKey(), entry.getValue()); + } + } + + public ObjectState objectState(EscapeRecord record) { + assert recordStates.containsKey(record); + return recordStates.get(record); + } + + public ObjectState objectState(ValueNode value) { + EscapeRecord record = recordAliases.get(value); + return record == null ? null : objectState(record); + } + + @Override + public BlockState clone() { + return new BlockState(this); + } + + public void materializeBefore(FixedNode fixed, EscapeRecord record) { + if (changeGraph) { + HashSet deferred = new HashSet<>(); + ArrayList deferredStores = new ArrayList<>(); + materializeBefore(fixed, record, deferred, deferredStores); + for (FixedWithNextNode write : deferredStores) { + write.setProbability(fixed.probability()); + graph.addBeforeFixed(fixed, write); + } + } else { + materializeUnchangedBefore(record); + } + } + + private void materializeUnchangedBefore(EscapeRecord record) { + trace("materializing %s", record); + ObjectState obj = objectState(record); + if (obj.lockCount > 0) { + if (changeGraph) { + error("object materialized with lock: %s\n", record); + } + throw new BailoutException("object materialized with lock"); + } + + ValueNode[] fieldState = obj.fieldState; + obj.fieldState = null; + obj.materializedValue = DUMMY_NODE; + for (int i = 0; i < fieldState.length; i++) { + ObjectState valueObj = objectState(fieldState[i]); + if (valueObj != null) { + if (valueObj.materializedValue == null) { + materializeUnchangedBefore(valueObj.record); + } + } + } + obj.initialized = true; + } + + private void materializeBefore(FixedNode fixed, EscapeRecord record, HashSet deferred, ArrayList deferredStores) { + trace("materializing %s at %s", record, fixed); + ObjectState obj = objectState(record); + if (obj.lockCount > 0) { + error("object materialized with lock: %s\n", record); + throw new BailoutException("object materialized with lock"); + } + + MaterializeObjectNode materialize = graph.add(new MaterializeObjectNode(record.type, record.fields)); + materialize.setProbability(fixed.probability()); + ValueNode[] fieldState = obj.fieldState; + metricMaterializations.increment(); + metricMaterializationFields.add(fieldState.length); + obj.fieldState = null; + obj.materializedValue = materialize; + deferred.add(record); + for (int i = 0; i < fieldState.length; i++) { + ObjectState valueObj = objectState(fieldState[i]); + if (valueObj != null) { + if (valueObj.materializedValue == null) { + materializeBefore(fixed, valueObj.record, deferred, deferredStores); + } + if (deferred.contains(valueObj.record)) { + if (record.type.isArrayClass()) { + deferredStores.add(graph.add(new StoreIndexedNode(materialize, ConstantNode.forInt(i, graph), record.type.componentType().kind(), valueObj.materializedValue, -1))); + } else { + deferredStores.add(graph.add(new StoreFieldNode(materialize, (ResolvedJavaField) record.fields[i].representation(), valueObj.materializedValue, -1))); + } + materialize.values().set(i, ConstantNode.defaultForKind(record.fields[i].type().kind(), graph)); + } else { + assert valueObj.initialized : "should be initialized: " + record + " at " + fixed; + materialize.values().set(i, valueObj.materializedValue); + } + } else { + materialize.values().set(i, fieldState[i]); + } + } + deferred.remove(record); + + obj.initialized = true; + graph.addBeforeFixed(fixed, materialize); + } + + private void addAndMarkAlias(EscapeRecord record, ValueNode node) { + addAlias(record, node); + for (Node usage : node.usages()) { + assert !visitedNodes.isMarked(usage) : "used by already visited node: " + node + " -> " + usage; + usages.mark(usage); + if (usage instanceof VirtualState) { + markVirtualUsages(usage); + } + } + obsoleteNodes.add(node); + } + + private void markVirtualUsages(Node node) { + usages.mark(node); + if (node instanceof VirtualState) { + for (Node usage : node.usages()) { + markVirtualUsages(usage); + } + } + } + + public void addAlias(EscapeRecord record, ValueNode alias) { + recordAliases.put(alias, record); + } + + public void addRecord(EscapeRecord record, ObjectState state) { + recordStates.put(record, state); + } + + public Iterable states() { + return recordStates.values(); + } + + @Override + public String toString() { + return recordStates.toString(); + } + } + + private class PartialEscapeIterator extends PostOrderBlockIterator { + + public PartialEscapeIterator(StructuredGraph graph, Block start) { + super(graph, start, new BlockState()); + } + + @Override + protected void processBlock(Block block, BlockState state) { + trace("\nBlock: %s (", block); + List nodeList = schedule.getBlockToNodesMap().get(block); + + FixedWithNextNode lastFixedNode = null; + for (Node node : nodeList) { + EscapeOp op = null; + if (node instanceof EscapeAnalyzable) { + op = ((EscapeAnalyzable) node).getEscapeOp(); + } + if (op != null) { + // only escape analyze allocations that were escape analyzed during the first iteration + if (changeGraph && !allocations.contains(node)) { + op = null; + } + } + + if (op != null) { + changed = true; + trace("{{%s}} ", node); + ResolvedJavaType type = op.type(node); + EscapeField[] fields = op.fields(node); + VirtualObjectNode virtualObject = changeGraph ? graph.add(new VirtualObjectNode(virtualIds, type, fields.length)) : null; + EscapeRecord record = new EscapeRecord(type, fields, virtualObject); + ValueNode[] fieldState = new ValueNode[fields.length]; + if (changeGraph) { + for (int i = 0; i < fields.length; i++) { + fieldState[i] = ConstantNode.defaultForKind(fields[i].type().kind(), virtualObject.graph()); + } + metricAllocationRemoved.increment(); + metricAllocationFieldsRemoved.add(fieldState.length); + } else { + allocations.add((ValueNode) node); + } + state.addRecord(record, new ObjectState(record, fieldState, 0)); + state.addAndMarkAlias(record, (ValueNode) node); + virtualIds++; + } else { + visitedNodes.mark(node); + + if (changeGraph && node instanceof LoopExitNode) { + for (ObjectState obj : state.states()) { + if (obj.fieldState != null) { + for (int i = 0; i < obj.fieldState.length; i++) { + ValueNode value = obj.fieldState[i]; + ObjectState valueObj = state.objectState(value); + if (valueObj == null) { + obj.fieldState[i] = graph.unique(new ValueProxyNode(value, (LoopExitNode) node, PhiType.Value)); + } + } + } else { + obj.materializedValue = graph.unique(new ValueProxyNode(obj.materializedValue, (LoopExitNode) node, PhiType.Value)); + } + } + } + + if (usages.isMarked(node)) { + trace("[[%s]] ", node); + processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state); + } else { + trace("%s ", node); + } + } + + if (node instanceof FixedWithNextNode && node.isAlive()) { + lastFixedNode = (FixedWithNextNode) node; + } + } + trace(")\n end state: %s\n", state); + } + + private void processNode(final ValueNode node, FixedNode insertBefore, final BlockState state) { + boolean usageFound = false; + if (node instanceof PiNode || node instanceof ValueProxyNode) { + ValueNode value = node instanceof PiNode ? ((PiNode) node).object() : ((ValueProxyNode) node).value(); + ObjectState obj = state.objectState(value); + assert obj != null : node; + if (obj.materializedValue == null) { + state.addAndMarkAlias(obj.record, node); + } else { + if (changeGraph) { + node.replaceFirstInput(value, obj.materializedValue); + } + } + usageFound = true; + } else if (node instanceof CheckCastNode) { + CheckCastNode x = (CheckCastNode) node; + ObjectState obj = state.objectState(x.object()); + assert obj != null : x; + if (obj.materializedValue == null) { + if (x.targetClass() != null && obj.record.type.isSubtypeOf(x.targetClass())) { + metricOtherRemoved.increment(); + state.addAndMarkAlias(obj.record, x); + // throw new UnsupportedOperationException("probably incorrect - losing dependency"); + } else { + replaceWithMaterialized(x.object(), x, state, obj); + } + } else { + if (changeGraph) { + node.replaceFirstInput(x.object(), obj.materializedValue); + } + } + usageFound = true; + } else if (node instanceof IsNullNode) { + IsNullNode x = (IsNullNode) node; + ObjectState obj = state.objectState(x.object()); + assert obj != null : x; + if (changeGraph) { + graph.replaceFloating(x, graph.unique(ConstantNode.forBoolean(false, graph))); + metricOtherRemoved.increment(); + } + usageFound = true; + } else if (node instanceof IsTypeNode) { + throw new GraalInternalError("a newly created object can never be an object hub"); + } else if (node instanceof AccessMonitorNode) { + AccessMonitorNode x = (AccessMonitorNode) node; + ObjectState obj = state.objectState(x.object()); + if (obj != null) { + Debug.log("monitor operation %s on %s\n", x, obj.record); + if (node instanceof MonitorEnterNode) { + obj.lockCount++; + } else { + assert node instanceof MonitorExitNode; + obj.lockCount--; + } + if (changeGraph) { + if (obj.materializedValue == null) { + metricLockRemoved.increment(); + node.replaceFirstInput(x.object(), obj.record.virtualObject); + x.eliminate(); + } else { + node.replaceFirstInput(x.object(), obj.materializedValue); + } + } + usageFound = true; + } + } else if (node instanceof LoadFieldNode) { + LoadFieldNode x = (LoadFieldNode) node; + ObjectState obj = state.objectState(x.object()); + assert obj != null : x; + if (!obj.record.fieldMap.containsKey(x.field())) { + // the field does not exist in the virtual object + ensureMaterialized(state, obj, x); + } + if (obj.materializedValue == null) { + int index = obj.record.fieldMap.get(x.field()); + ValueNode result = obj.fieldState[index]; + ObjectState resultObj = state.objectState(result); + if (resultObj != null) { + state.addAndMarkAlias(resultObj.record, x); + } else { + if (changeGraph) { + x.replaceAtUsages(result); + graph.removeFixed(x); + } + } + if (changeGraph) { + metricLoadRemoved.increment(); + } + } else { + if (changeGraph) { + x.replaceFirstInput(x.object(), obj.materializedValue); + } + } + usageFound = true; + } else if (node instanceof StoreFieldNode) { + StoreFieldNode x = (StoreFieldNode) node; + ValueNode object = x.object(); + ValueNode value = x.value(); + ObjectState obj = state.objectState(object); + if (obj != null) { + if (!obj.record.fieldMap.containsKey(x.field())) { + // the field does not exist in the virtual object + ensureMaterialized(state, obj, x); + } + if (obj.materializedValue == null) { + int index = obj.record.fieldMap.get(x.field()); + obj.fieldState[index] = value; + if (changeGraph) { + graph.removeFixed(x); + metricStoreRemoved.increment(); + } + } else { + if (changeGraph) { + x.replaceFirstInput(object, obj.materializedValue); + } + ObjectState valueObj = state.objectState(value); + if (valueObj != null) { + replaceWithMaterialized(value, x, state, valueObj); + } + } + usageFound = true; + } else { + ObjectState valueObj = state.objectState(value); + if (valueObj != null) { + replaceWithMaterialized(value, x, state, valueObj); + usageFound = true; + } + } + } else if (node instanceof LoadIndexedNode) { + LoadIndexedNode x = (LoadIndexedNode) node; + ValueNode array = x.array(); + ObjectState arrayObj = state.objectState(array); + if (arrayObj != null) { + if (arrayObj.materializedValue == null) { + int index = x.index().isConstant() ? x.index().asConstant().asInt() : -1; + if (index < 0 || index >= arrayObj.fieldState.length) { + // out of bounds or not constant + replaceWithMaterialized(array, x, state, arrayObj); + } else { + ValueNode result = arrayObj.fieldState[index]; + ObjectState resultObj = state.objectState(result); + if (resultObj != null) { + state.addAndMarkAlias(resultObj.record, x); + } else { + if (changeGraph) { + x.replaceAtUsages(result); + graph.removeFixed(x); + } + } + if (changeGraph) { + metricLoadRemoved.increment(); + } + } + } else { + if (changeGraph) { + x.replaceFirstInput(array, arrayObj.materializedValue); + } + } + usageFound = true; + } + } else if (node instanceof StoreIndexedNode) { + StoreIndexedNode x = (StoreIndexedNode) node; + ValueNode array = x.array(); + ValueNode value = x.value(); + ObjectState arrayObj = state.objectState(array); + ObjectState valueObj = state.objectState(value); + + if (arrayObj != null) { + if (arrayObj.materializedValue == null) { + int index = x.index().isConstant() ? x.index().asConstant().asInt() : -1; + if (index < 0 || index >= arrayObj.fieldState.length) { + // out of bounds or not constant + replaceWithMaterialized(array, x, state, arrayObj); + } else { + arrayObj.fieldState[index] = value; + if (changeGraph) { + graph.removeFixed(x); + metricStoreRemoved.increment(); + } + } + } else { + if (changeGraph) { + x.replaceFirstInput(array, arrayObj.materializedValue); + } + if (valueObj != null) { + replaceWithMaterialized(value, x, state, valueObj); + } + } + usageFound = true; + } else { + if (valueObj != null) { + replaceWithMaterialized(value, x, state, valueObj); + usageFound = true; + } + } + } else if (node instanceof RegisterFinalizerNode) { + RegisterFinalizerNode x = (RegisterFinalizerNode) node; + ObjectState obj = state.objectState(x.object()); + replaceWithMaterialized(x.object(), x, state, obj); + usageFound = true; + } else if (node instanceof ArrayLengthNode) { + ArrayLengthNode x = (ArrayLengthNode) node; + ObjectState obj = state.objectState(x.array()); + assert obj != null : x; + if (changeGraph) { + graph.replaceFixedWithFloating(x, ConstantNode.forInt(obj.record.fields.length, graph)); + metricOtherRemoved.increment(); + } + usageFound = true; + } else if (node instanceof ReadHubNode) { + ReadHubNode x = (ReadHubNode) node; + ObjectState obj = state.objectState(x.object()); + assert obj != null : x; + if (changeGraph) { + ConstantNode hub = ConstantNode.forConstant(obj.record.type.getEncoding(Representation.ObjectHub), runtime, graph); + graph.replaceFixedWithFloating(x, hub); + metricOtherRemoved.increment(); + } + usageFound = true; + } else if (node instanceof ReturnNode) { + ReturnNode x = (ReturnNode) node; + ObjectState obj = state.objectState(x.result()); + replaceWithMaterialized(x.result(), x, state, obj); + usageFound = true; + } else if (node instanceof MethodCallTargetNode) { + for (ValueNode argument : ((MethodCallTargetNode) node).arguments()) { + ObjectState obj = state.objectState(argument); + if (obj != null) { + replaceWithMaterialized(argument, node, insertBefore, state, obj); + usageFound = true; + } + } + } else if (node instanceof ObjectEqualsNode) { + ObjectEqualsNode x = (ObjectEqualsNode) node; + ObjectState xObj = state.objectState(x.x()); + ObjectState yObj = state.objectState(x.y()); + boolean xVirtual = xObj != null && xObj.materializedValue == null; + boolean yVirtual = yObj != null && yObj.materializedValue == null; + + if (changeGraph) { + if (xVirtual ^ yVirtual) { + // one of them is virtual: they can never be the same objects + graph.replaceFloating(x, ConstantNode.forBoolean(false, graph)); + usageFound = true; + metricOtherRemoved.increment(); + } else if (xVirtual && yVirtual) { + // both are virtual: check if they refer to the same object + graph.replaceFloating(x, ConstantNode.forBoolean(xObj == yObj, graph)); + usageFound = true; + metricOtherRemoved.increment(); + } else { + assert xObj != null || yObj != null; + if (xObj != null) { + assert xObj.materializedValue != null; + node.replaceFirstInput(x.x(), xObj.materializedValue); + } + if (yObj != null) { + assert yObj.materializedValue != null; + node.replaceFirstInput(x.y(), yObj.materializedValue); + } + } + } + usageFound = true; + } else if (node instanceof MergeNode) { + usageFound = true; + } else if (node instanceof UnsafeLoadNode || node instanceof UnsafeStoreNode || node instanceof CompareAndSwapNode || node instanceof SafeReadNode) { + for (ValueNode input : node.inputs().filter(ValueNode.class)) { + ObjectState obj = state.objectState(input); + if (obj != null) { + replaceWithMaterialized(input, node, insertBefore, state, obj); + usageFound = true; + } + } + } + if (node.isAlive() && node instanceof StateSplit) { + StateSplit split = (StateSplit) node; + FrameState stateAfter = split.stateAfter(); + if (stateAfter != null) { + if (changeGraph) { + if (stateAfter.usages().size() > 1) { + stateAfter = (FrameState) stateAfter.copyWithInputs(); + split.setStateAfter(stateAfter); + } + final HashSet virtual = new HashSet<>(); + stateAfter.applyToNonVirtual(new NodeClosure() { + + @Override + public void apply(Node usage, ValueNode value) { + ObjectState valueObj = state.objectState(value); + if (valueObj != null) { + virtual.add(valueObj); + usage.replaceFirstInput(value, valueObj.record.virtualObject); + } else if (value instanceof VirtualObjectNode) { + ObjectState virtualObj = null; + for (ObjectState obj : state.states()) { + if (value == obj.record.virtualObject) { + virtualObj = obj; + break; + } + } + assert virtualObj != null; + virtual.add(virtualObj); + } + } + }); + + ArrayDeque queue = new ArrayDeque<>(virtual); + while (!queue.isEmpty()) { + ObjectState obj = queue.removeLast(); + if (obj.materializedValue == null) { + for (ValueNode field : obj.fieldState) { + ObjectState fieldObj = state.objectState(field); + if (fieldObj != null) { + if (fieldObj.materializedValue == null && !virtual.contains(fieldObj)) { + virtual.add(fieldObj); + queue.addLast(fieldObj); + } + } + } + } + } + for (ObjectState obj : virtual) { + EscapeObjectState v; + if (obj.materializedValue == null) { + ValueNode[] fieldState = obj.fieldState.clone(); + for (int i = 0; i < fieldState.length; i++) { + ObjectState valueObj = state.objectState(fieldState[i]); + if (valueObj != null) { + if (valueObj.materializedValue == null) { + fieldState[i] = valueObj.record.virtualObject; + } else { + fieldState[i] = valueObj.materializedValue; + } + } + } + v = graph.add(new VirtualObjectState(obj.record.virtualObject, fieldState)); + } else { + v = graph.add(new MaterializedObjectState(obj.record.virtualObject, obj.materializedValue)); + } + for (EscapeObjectState s : stateAfter.virtualObjectMappings()) { + if (s.object() == v.object()) { + throw new GraalInternalError("unexpected duplicate virtual state at: %s for %s", node, v.object()); + } + } + stateAfter.addVirtualObjectMapping(v); + } + } + usageFound = true; + } + } + if (!usageFound) { + for (ValueNode input : node.inputs().filter(ValueNode.class)) { + ObjectState obj = state.objectState(input); + if (obj != null) { + replaceWithMaterialized(input, node, insertBefore, state, obj); + usageFound = true; + } + } + Debug.log("unexpected usage of %s: %s\n", node, node.inputs().snapshot()); + } + } + + private void ensureMaterialized(BlockState state, ObjectState obj, FixedNode materializeBefore) { + assert obj != null; + if (obj.materializedValue == null) { + state.materializeBefore(materializeBefore, obj.record); + } + assert obj.materializedValue != null; + } + + private void replaceWithMaterialized(ValueNode value, FixedNode usage, BlockState state, ObjectState obj) { + ensureMaterialized(state, obj, usage); + if (changeGraph) { + usage.replaceFirstInput(value, obj.materializedValue); + } + } + + private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockState state, ObjectState obj) { + ensureMaterialized(state, obj, materializeBefore); + if (changeGraph) { + usage.replaceFirstInput(value, obj.materializedValue); + } + } + + @Override + protected BlockState merge(MergeNode merge, List states) { + BlockState newState = new BlockState(); + + newState.recordAliases.putAll(states.get(0).recordAliases); + for (int i = 1; i < states.size(); i++) { + BlockState state = states.get(i); + for (Map.Entry entry : states.get(0).recordAliases.entrySet()) { + if (state.recordAliases.containsKey(entry.getKey())) { + assert state.recordAliases.get(entry.getKey()) == entry.getValue(); + } else { + newState.recordAliases.remove(entry.getKey()); + } + } + } + + // Iterative processing: + // Merging the materialized/virtual state of virtual objects can lead to new materializations, which can + // lead to new materializations because of phis, and so on. + + boolean materialized; + do { + materialized = false; + // use a hash set to make the values distinct... + for (EscapeRecord record : new HashSet<>(newState.recordAliases.values())) { + ObjectState resultState = newState.recordStates.get(record); + if (resultState == null || resultState.materializedValue == null) { + int virtual = 0; + int lockCount = states.get(0).objectState(record).lockCount; + for (BlockState state : states) { + ObjectState obj = state.objectState(record); + if (obj.materializedValue == null) { + virtual++; + } + assert obj.lockCount == lockCount : "mismatching lock counts"; + } + + if (virtual < states.size()) { + ValueNode materializedValuePhi = changeGraph ? graph.add(new PhiNode(Kind.Object, merge)) : DUMMY_NODE; + for (int i = 0; i < states.size(); i++) { + BlockState state = states.get(i); + ObjectState obj = state.objectState(record); + materialized |= obj.materializedValue == null; + ensureMaterialized(state, obj, merge.forwardEndAt(i)); + if (changeGraph) { + ((PhiNode) materializedValuePhi).addInput(obj.materializedValue); + } + } + newState.addRecord(record, new ObjectState(record, materializedValuePhi, lockCount)); + } else { + assert virtual == states.size(); + ValueNode[] values = states.get(0).objectState(record).fieldState.clone(); + PhiNode[] phis = new PhiNode[values.length]; + boolean[] phiCreated = new boolean[values.length]; + int mismatch = 0; + for (int i = 1; i < states.size(); i++) { + BlockState state = states.get(i); + ValueNode[] fields = state.objectState(record).fieldState; + for (int index = 0; index < values.length; index++) { + if (!phiCreated[index] && values[index] != fields[index]) { + mismatch++; + if (changeGraph) { + phis[index] = graph.add(new PhiNode(values[index].kind(), merge)); + } + phiCreated[index] = true; + } + } + } + if (mismatch > 0) { + for (int i = 0; i < states.size(); i++) { + BlockState state = states.get(i); + ValueNode[] fields = state.objectState(record).fieldState; + for (int index = 0; index < values.length; index++) { + if (phiCreated[index]) { + ObjectState obj = state.objectState(fields[index]); + if (obj != null) { + materialized |= obj.materializedValue == null; + ensureMaterialized(state, obj, merge.forwardEndAt(i)); + fields[index] = obj.materializedValue; + } + if (changeGraph) { + phis[index].addInput(fields[index]); + } + } + } + } + for (int index = 0; index < values.length; index++) { + if (phiCreated[index]) { + values[index] = phis[index]; + } + } + } + newState.addRecord(record, new ObjectState(record, values, lockCount)); + } + } + } + + for (PhiNode phi : merge.phis().snapshot()) { + if (usages.isMarked(phi) && phi.type() == PhiType.Value) { + visitedNodes.mark(phi); + materialized |= processPhi(newState, merge, phi, states); + } + } + } while (materialized); + + return newState; + } + + private boolean processPhi(BlockState newState, MergeNode merge, PhiNode phi, List states) { + assert states.size() == phi.valueCount(); + int virtualInputs = 0; + boolean materialized = false; + EscapeRecord sameRecord = null; + ResolvedJavaType sameType = null; + int sameFieldCount = -1; + for (int i = 0; i < phi.valueCount(); i++) { + ValueNode value = phi.valueAt(i); + ObjectState obj = states.get(i).objectState(value); + if (obj != null) { + if (obj.materializedValue == null) { + virtualInputs++; + if (i == 0) { + sameRecord = obj.record; + sameType = obj.record.type; + sameFieldCount = obj.record.fields.length; + } else { + if (sameRecord != obj.record) { + sameRecord = null; + } + if (sameType != obj.record.type) { + sameType = null; + } + if (sameFieldCount != obj.record.fields.length) { + sameFieldCount = -1; + } + } + } else { + if (changeGraph) { + phi.setValueAt(i, obj.materializedValue); + } + } + } + } + boolean materialize = false; + if (virtualInputs == 0) { + // nothing to do... + } else if (virtualInputs == phi.valueCount()) { + if (sameRecord != null) { + newState.addAndMarkAlias(sameRecord, phi); + } else if (sameType != null && sameFieldCount != -1) { + materialize = true; + // throw new GraalInternalError("merge required for %s", sameType); + } else { + materialize = true; + } + } else { + materialize = true; + } + + if (materialize) { + for (int i = 0; i < phi.valueCount(); i++) { + ValueNode value = phi.valueAt(i); + ObjectState obj = states.get(i).objectState(value); + if (obj != null) { + materialized |= obj.materializedValue == null; + replaceWithMaterialized(value, phi, merge.forwardEndAt(i), states.get(i), obj); + } + } + } + return materialized; + } + + @Override + protected BlockState loopBegin(LoopBeginNode loopBegin, BlockState beforeLoopState) { + BlockState state = beforeLoopState; + for (ObjectState obj : state.states()) { + if (obj.fieldState != null) { + for (int i = 0; obj.fieldState != null && i < obj.fieldState.length; i++) { + ValueNode value = obj.fieldState[i]; + ObjectState valueObj = state.objectState(value); + if (valueObj != null) { + ensureMaterialized(state, valueObj, loopBegin.forwardEnd()); + value = valueObj.materializedValue; + } + } + } + } + for (ObjectState obj : state.states()) { + if (obj.fieldState != null) { + for (int i = 0; i < obj.fieldState.length; i++) { + ValueNode value = obj.fieldState[i]; + ObjectState valueObj = state.objectState(value); + if (valueObj != null) { + value = valueObj.materializedValue; + } + if (changeGraph) { + assert value != null; + PhiNode valuePhi = graph.add(new PhiNode(value.kind(), loopBegin)); + valuePhi.addInput(value); + obj.fieldState[i] = valuePhi; + } + } + } + } + for (PhiNode phi : loopBegin.phis()) { + ObjectState obj = state.objectState(phi.valueAt(0)); + if (obj != null) { + ensureMaterialized(state, obj, loopBegin.forwardEnd()); + if (changeGraph) { + phi.setValueAt(0, obj.materializedValue); + } + } + } + return state.clone(); + } + + @Override + protected BlockState loopEnds(LoopBeginNode loopBegin, BlockState loopBeginState, List loopEndStates) { + BlockState state = loopBeginState.clone(); + List loopEnds = loopBegin.orderedLoopEnds(); + for (ObjectState obj : state.states()) { + if (obj.fieldState != null) { + Iterator iter = loopEnds.iterator(); + for (BlockState loopEndState : loopEndStates) { + LoopEndNode loopEnd = iter.next(); + ObjectState endObj = loopEndState.objectState(obj.record); + if (endObj.fieldState == null) { + if (changeGraph) { + error("object materialized within loop: %s\n", obj.record); + } + throw new BailoutException("object materialized within loop"); + } + for (int i = 0; endObj.fieldState != null && i < endObj.fieldState.length; i++) { + ValueNode value = endObj.fieldState[i]; + ObjectState valueObj = loopEndState.objectState(value); + if (valueObj != null) { + ensureMaterialized(loopEndState, valueObj, loopEnd); + value = valueObj.materializedValue; + } + if (changeGraph) { + ((PhiNode) obj.fieldState[i]).addInput(value); + } + } + } + } + } + for (PhiNode phi : loopBegin.phis()) { + if (phi.valueCount() == 1) { + if (changeGraph) { + phi.replaceAtUsages(phi.valueAt(0)); + } + } else { + assert phi.valueCount() == loopEndStates.size() + 1; + for (int i = 0; i < loopEndStates.size(); i++) { + BlockState loopEndState = loopEndStates.get(i); + ObjectState obj = loopEndState.objectState(phi.valueAt(i + 1)); + if (obj != null) { + ensureMaterialized(loopEndState, obj, loopEnds.get(i)); + if (changeGraph) { + phi.setValueAt(i + 1, obj.materializedValue); + } + } + } + } + } + return state; + } + + @Override + protected BlockState afterSplit(FixedNode node, BlockState oldState) { + return oldState.clone(); + } + } +} + +public class PartialEscapeAnalysisPhase extends Phase { + + private final TargetDescription target; + private final GraalCodeCacheProvider runtime; + private final Assumptions assumptions; + + public PartialEscapeAnalysisPhase(TargetDescription target, GraalCodeCacheProvider runtime, Assumptions assumptions) { + this.runtime = runtime; + this.target = target; + this.assumptions = assumptions; + } + + @Override + protected void run(StructuredGraph graph) { + HashSet allocations = new HashSet<>(); + SchedulePhase schedule = new SchedulePhase(); + schedule.apply(graph, false); + try { + new EscapeAnalysisIteration(graph, schedule, runtime, allocations, false).run(); + } catch (BailoutException e) { + // do nothing if the if the escape analysis bails out during the analysis iteration... + return; + } + try { + new EscapeAnalysisIteration(graph, schedule, runtime, allocations, true).run(); + new CanonicalizerPhase(target, runtime, assumptions).apply(graph); + } catch (BailoutException e) { + throw new GraalInternalError(e); + } + } + + public static boolean isValidConstantIndex(AccessIndexedNode x) { + Constant index = x.index().asConstant(); + if (x.array() instanceof NewArrayNode) { + Constant length = ((NewArrayNode) x.array()).dimension(0).asConstant(); + return index != null && length != null && index.asInt() >= 0 && index.asInt() < length.asInt(); + } else { + return false; + } + } +} diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Thu Sep 13 10:45:17 2012 +0200 @@ -83,7 +83,8 @@ // set the final fields compilerToVm = toVM; vmToCompiler = toCompiler; - config = compilerToVm.getConfiguration(); + config = new HotSpotVMConfig(); + compilerToVm.initializeConfiguration(config); config.check(); if (Boolean.valueOf(System.getProperty("graal.printconfig"))) { diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Thu Sep 13 10:45:17 2012 +0200 @@ -22,6 +22,10 @@ */ package com.oracle.graal.hotspot; +import com.oracle.graal.api.code.*; +import com.oracle.max.asm.target.amd64.*; + + /** * Used to communicate configuration details, runtime offsets, etc. to graal upon compileMethod. */ @@ -29,7 +33,7 @@ private static final long serialVersionUID = -4744897993263044184L; - private HotSpotVMConfig() { + HotSpotVMConfig() { } // os information, register layout, code generation, ... @@ -56,6 +60,11 @@ public int hubOffset; /** + * The offset of the _prototype_header field in a Klass. + */ + public int initialMarkWordOffset; + + /** * The offset of an the array length in an array's header. */ public int arrayLengthOffset; @@ -102,8 +111,6 @@ public int threadObjectOffset; - public int instanceHeaderPrototypeOffset; - public int threadExceptionOopOffset; public int threadExceptionPcOffset; public int threadMultiNewArrayStorageOffset; @@ -147,6 +154,9 @@ public long fastMonitorExitStub; public long verifyOopStub; + // special registers + public final Register threadRegister = AMD64.r15; + public void check() { assert vmPageSize >= 16; assert codeEntryAlignment > 0; diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Thu Sep 13 10:45:17 2012 +0200 @@ -65,7 +65,7 @@ HotSpotCompiledMethod installMethod(HotSpotCompilationResult compResult, boolean makeDefault, HotSpotCodeInfo info); - HotSpotVMConfig getConfiguration(); + void initializeConfiguration(HotSpotVMConfig config); JavaMethod JavaType_resolveMethodImpl(HotSpotResolvedJavaType klass, String name, String signature); @@ -117,5 +117,7 @@ String decodePC(long pc); + long JavaType_initialMarkWord(HotSpotResolvedJavaType hotSpotResolvedJavaType); + // Checkstyle: resume } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Thu Sep 13 10:45:17 2012 +0200 @@ -80,7 +80,7 @@ public native HotSpotCompiledMethod installMethod(HotSpotCompilationResult comp, boolean makeDefault, HotSpotCodeInfo info); @Override - public native HotSpotVMConfig getConfiguration(); + public native void initializeConfiguration(HotSpotVMConfig config); @Override public native JavaMethod JavaType_resolveMethodImpl(HotSpotResolvedJavaType klass, String name, String signature); @@ -167,5 +167,8 @@ @Override public native String decodePC(long pc); + @Override + public native long JavaType_initialMarkWord(HotSpotResolvedJavaType type); + // Checkstyle: resume } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java Thu Sep 13 10:45:17 2012 +0200 @@ -55,11 +55,6 @@ private boolean isInitialized; private ResolvedJavaType arrayOfType; - /** - * Initial value for the mark word in a new object of this type. - */ - private long initialMarkWord; - private HotSpotResolvedJavaType() { throw new GraalInternalError(HotSpotResolvedJavaType.class + " should only be created from C++ code"); } @@ -294,6 +289,6 @@ } public long initialMarkWord() { - return initialMarkWord; + return HotSpotGraalRuntime.getInstance().getCompilerToVM().JavaType_initialMarkWord(this); } } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentThread.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentThread.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentThread.java Thu Sep 13 10:45:17 2012 +0200 @@ -22,9 +22,9 @@ */ package com.oracle.graal.hotspot.nodes; -import com.oracle.max.asm.target.amd64.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.hotspot.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -39,8 +39,9 @@ } @Override - public void generate(LIRGeneratorTool generator) { - generator.setResult(this, generator.emitLoad(new Address(Kind.Object, AMD64.r15.asValue(generator.target().wordKind), threadObjectOffset), false)); + public void generate(LIRGeneratorTool gen) { + Register thread = HotSpotGraalRuntime.getInstance().getConfig().threadRegister; + gen.setResult(this, gen.emitLoad(new Address(Kind.Object, thread.asValue(gen.target().wordKind), threadObjectOffset), false)); } @SuppressWarnings("unused") diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java Thu Sep 13 10:45:17 2012 +0200 @@ -21,6 +21,8 @@ * questions. */ package com.oracle.graal.hotspot.snippets; +import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; + import com.oracle.graal.api.meta.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.nodes.*; @@ -209,35 +211,4 @@ } } } - - @Fold - static int arrayBaseOffset(Kind elementKind) { - return elementKind.getArrayBaseOffset(); - } - - @Fold - static int arrayIndexScale(Kind elementKind) { - return elementKind.getArrayIndexScale(); - } - - static { - assert arrayIndexScale(Kind.Byte) == 1; - assert arrayIndexScale(Kind.Boolean) == 1; - assert arrayIndexScale(Kind.Char) == 2; - assert arrayIndexScale(Kind.Short) == 2; - assert arrayIndexScale(Kind.Int) == 4; - assert arrayIndexScale(Kind.Long) == 8; - assert arrayIndexScale(Kind.Float) == 4; - assert arrayIndexScale(Kind.Double) == 8; - } - - @Fold - private static int cardTableShift() { - return HotSpotGraalRuntime.getInstance().getConfig().cardtableShift; - } - - @Fold - private static long cardTableStart() { - return HotSpotGraalRuntime.getInstance().getConfig().cardtableStartAddress; - } } diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java Thu Sep 13 10:45:17 2012 +0200 @@ -21,7 +21,7 @@ * questions. */ package com.oracle.graal.hotspot.snippets; -import static com.oracle.graal.hotspot.snippets.ArrayCopySnippets.*; +import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; import static com.oracle.graal.snippets.Snippet.Varargs.*; import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*; @@ -37,7 +37,6 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; -import com.oracle.graal.snippets.Snippet.Fold; import com.oracle.graal.snippets.Snippet.Parameter; import com.oracle.graal.snippets.Snippet.VarargsParameter; import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates; @@ -65,7 +64,7 @@ isNull.inc(); return object; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (objectHub != exactHub) { exactMiss.inc(); DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.ClassCastException); @@ -91,7 +90,7 @@ isNull.inc(); return object; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (UnsafeLoadNode.loadObject(objectHub, 0, superCheckOffset, true) != hub) { displayMiss.inc(); DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.ClassCastException); @@ -113,7 +112,7 @@ isNull.inc(); return object; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -143,7 +142,7 @@ isNull.inc(); return object; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -232,26 +231,6 @@ return false; } - @Fold - private static int superCheckOffsetOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().superCheckOffsetOffset; - } - - @Fold - private static int secondarySuperCacheOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().secondarySuperCacheOffset; - } - - @Fold - private static int secondarySupersOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().secondarySupersOffset; - } - - @Fold - private static int hubOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().hubOffset; - } - public static class Templates extends AbstractTemplates { private final ResolvedJavaMethod exact; diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/HotSpotSnippetUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/HotSpotSnippetUtils.java Thu Sep 13 10:45:17 2012 +0200 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.hotspot.snippets; + +import static com.oracle.graal.nodes.extended.UnsafeLoadNode.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.code.Register.RegisterFlag; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.snippets.Snippet.Fold; +import com.oracle.graal.snippets.*; +import com.oracle.max.asm.target.amd64.*; + +//JaCoCo Exclude + +/** + * A collection of methods used in HotSpot snippets. + */ +public class HotSpotSnippetUtils { + + @Fold + static boolean verifyOops() { + return HotSpotGraalRuntime.getInstance().getConfig().verifyOops; + } + + @Fold + static int threadTlabTopOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().threadTlabTopOffset; + } + + @Fold + static int threadTlabEndOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().threadTlabEndOffset; + } + + @Fold + static Kind wordKind() { + return HotSpotGraalRuntime.getInstance().getTarget().wordKind; + } + + @Fold + static Register threadReg() { + return HotSpotGraalRuntime.getInstance().getConfig().threadRegister; + } + + @Fold + static Register stackPointerReg() { + return AMD64.rsp; + } + + @Fold + static int wordSize() { + return HotSpotGraalRuntime.getInstance().getTarget().wordSize; + } + + @Fold + static int pageSize() { + return HotSpotGraalRuntime.getInstance().getTarget().pageSize; + } + + @Fold + static int initialMarkWordOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().initialMarkWordOffset; + } + + @Fold + static int markOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().markOffset; + } + + @Fold + static int hubOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().hubOffset; + } + + @Fold + static int arrayLengthOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().arrayLengthOffset; + } + + @Fold + static int arrayBaseOffset(Kind elementKind) { + return elementKind.getArrayBaseOffset(); + } + + @Fold + static int arrayIndexScale(Kind elementKind) { + return elementKind.getArrayIndexScale(); + } + + @Fold + static int cardTableShift() { + return HotSpotGraalRuntime.getInstance().getConfig().cardtableShift; + } + + @Fold + static long cardTableStart() { + return HotSpotGraalRuntime.getInstance().getConfig().cardtableStartAddress; + } + + @Fold + static int superCheckOffsetOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().superCheckOffsetOffset; + } + + @Fold + static int secondarySuperCacheOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().secondarySuperCacheOffset; + } + + @Fold + static int secondarySupersOffset() { + return HotSpotGraalRuntime.getInstance().getConfig().secondarySupersOffset; + } + + /** + * Loads the hub from a object, null checking it first. + */ + static Object loadHub(Object object) { + return UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + } + + + static Object verifyOop(Object object) { + if (verifyOops()) { + VerifyOopStubCall.call(object); + } + return object; + } + + static Word asWord(Object object) { + return Word.fromObject(object); + } + + static Word loadWord(Word address, int offset) { + Object value = loadObject(address, 0, offset, true); + return asWord(value); + } + + static { + assert arrayIndexScale(Kind.Byte) == 1; + assert arrayIndexScale(Kind.Boolean) == 1; + assert arrayIndexScale(Kind.Char) == 2; + assert arrayIndexScale(Kind.Short) == 2; + assert arrayIndexScale(Kind.Int) == 4; + assert arrayIndexScale(Kind.Long) == 8; + assert arrayIndexScale(Kind.Float) == 4; + assert arrayIndexScale(Kind.Double) == 8; + } + + public static Register getStubParameterRegister(int index) { + RegisterConfig regConfig = HotSpotGraalRuntime.getInstance().getRuntime().getGlobalStubRegisterConfig(); + return regConfig.getCallingConventionRegisters(CallingConvention.Type.RuntimeCall, RegisterFlag.CPU)[index]; + } +} diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/InstanceOfSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/InstanceOfSnippets.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/InstanceOfSnippets.java Thu Sep 13 10:45:17 2012 +0200 @@ -23,6 +23,7 @@ package com.oracle.graal.hotspot.snippets; import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.*; import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.Templates.*; +import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; import static com.oracle.graal.snippets.Snippet.Varargs.*; import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*; import static com.oracle.graal.snippets.nodes.JumpNode.*; @@ -41,7 +42,6 @@ import com.oracle.graal.nodes.util.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; -import com.oracle.graal.snippets.Snippet.Fold; import com.oracle.graal.snippets.Snippet.Parameter; import com.oracle.graal.snippets.Snippet.VarargsParameter; import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates; @@ -73,7 +73,7 @@ isNull.inc(); return falseValue; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (objectHub != exactHub) { exactMiss.inc(); return falseValue; @@ -95,7 +95,7 @@ jump(IfNode.FALSE_EDGE); return; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (objectHub != exactHub) { exactMiss.inc(); jump(IfNode.FALSE_EDGE); @@ -120,7 +120,7 @@ isNull.inc(); return falseValue; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (UnsafeLoadNode.loadObject(objectHub, 0, superCheckOffset, true) != hub) { displayMiss.inc(); return falseValue; @@ -143,7 +143,7 @@ jump(IfNode.FALSE_EDGE); return; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); if (UnsafeLoadNode.loadObject(objectHub, 0, superCheckOffset, true) != hub) { displayMiss.inc(); jump(IfNode.FALSE_EDGE); @@ -169,7 +169,7 @@ isNull.inc(); return falseValue; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -199,7 +199,7 @@ jump(IfNode.FALSE_EDGE); return; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -233,7 +233,7 @@ isNull.inc(); return falseValue; } - Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true); + Object objectHub = loadHub(object); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -276,22 +276,6 @@ return false; } - @Fold - private static int secondarySuperCacheOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().secondarySuperCacheOffset; - } - - @Fold - private static int secondarySupersOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().secondarySupersOffset; - } - - @Fold - private static int hubOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().hubOffset; - } - - public static class Templates extends AbstractTemplates { private final ResolvedJavaMethod ifExact; diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java Thu Sep 13 10:45:17 2012 +0200 @@ -24,11 +24,10 @@ import static com.oracle.graal.hotspot.nodes.CastFromHub.*; import static com.oracle.graal.hotspot.nodes.RegisterNode.*; -import static com.oracle.graal.nodes.extended.UnsafeLoadNode.*; +import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*; import static com.oracle.graal.snippets.nodes.DirectObjectStoreNode.*; import static com.oracle.graal.snippets.nodes.ExplodeLoopNode.*; -import static com.oracle.max.asm.target.amd64.AMD64.*; import static com.oracle.max.criutils.UnsignedMath.*; import com.oracle.graal.api.code.*; @@ -43,7 +42,6 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; -import com.oracle.graal.snippets.Snippet.Fold; import com.oracle.graal.snippets.Snippet.Parameter; import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates; import com.oracle.graal.snippets.SnippetTemplate.Arguments; @@ -56,7 +54,7 @@ @Snippet public static Word allocate(@Parameter("size") int size) { - Word thread = asWord(register(r15, wordKind())); + Word thread = asWord(register(threadReg(), wordKind())); Word top = loadWord(thread, threadTlabTopOffset()); Word end = loadWord(thread, threadTlabEndOffset()); Word newTop = top.plus(size); @@ -155,22 +153,6 @@ return size & mask; } - private static Object verifyOop(Object object) { - if (verifyOops()) { - VerifyOopStubCall.call(object); - } - return object; - } - - private static Word asWord(Object object) { - return Word.fromObject(object); - } - - private static Word loadWord(Word address, int offset) { - Object value = loadObject(address, 0, offset, true); - return asWord(value); - } - /** * Maximum size of an object whose body is initialized by a sequence of * zero-stores to its fields. Larger objects have their bodies initialized @@ -179,10 +161,16 @@ private static final int MAX_UNROLLED_OBJECT_ZEROING_SIZE = 10 * wordSize(); /** + * Setting this to false causes (as yet inexplicable) crashes on lusearch. + */ + private static final boolean USE_STATIC_INITIAL_MARK_WORD = true; + + /** * Formats some allocated memory with an object header zeroes out the rest. */ - private static void formatObject(Object hub, int size, Word memory, Word headerPrototype, boolean fillContents) { - storeObject(memory, 0, markOffset(), headerPrototype); + private static void formatObject(Object hub, int size, Word memory, Word staticInitialMarkWord, boolean fillContents) { + Word initialMarkWord = USE_STATIC_INITIAL_MARK_WORD ? staticInitialMarkWord : loadWord(asWord(hub), initialMarkWordOffset()); + storeObject(memory, 0, markOffset(), initialMarkWord); storeObject(memory, 0, hubOffset(), hub); if (fillContents) { if (size <= MAX_UNROLLED_OBJECT_ZEROING_SIZE) { @@ -203,8 +191,8 @@ /** * Formats some allocated memory with an object header zeroes out the rest. */ - private static void formatArray(Object hub, int size, int length, int headerSize, Word memory, Word headerPrototype, boolean fillContents) { - storeObject(memory, 0, markOffset(), headerPrototype); + private static void formatArray(Object hub, int size, int length, int headerSize, Word memory, Word initialMarkWord, boolean fillContents) { + storeObject(memory, 0, markOffset(), initialMarkWord); storeObject(memory, 0, hubOffset(), hub); storeInt(memory, 0, arrayLengthOffset(), length); if (fillContents) { @@ -350,46 +338,6 @@ } } - @Fold - private static boolean verifyOops() { - return HotSpotGraalRuntime.getInstance().getConfig().verifyOops; - } - - @Fold - private static int threadTlabTopOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().threadTlabTopOffset; - } - - @Fold - private static int threadTlabEndOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().threadTlabEndOffset; - } - - @Fold - private static Kind wordKind() { - return HotSpotGraalRuntime.getInstance().getTarget().wordKind; - } - - @Fold - private static int wordSize() { - return HotSpotGraalRuntime.getInstance().getTarget().wordSize; - } - - @Fold - private static int markOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().markOffset; - } - - @Fold - private static int hubOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().hubOffset; - } - - @Fold - private static int arrayLengthOffset() { - return HotSpotGraalRuntime.getInstance().getConfig().arrayLengthOffset; - } - private static final SnippetCounter.Group countersNew = GraalOptions.SnippetCounters ? new SnippetCounter.Group("NewInstance") : null; private static final SnippetCounter new_seqInit = new SnippetCounter(countersNew, "tlabSeqInit", "TLAB alloc with unrolled zeroing"); private static final SnippetCounter new_loopInit = new SnippetCounter(countersNew, "tlabLoopInit", "TLAB alloc with zeroing in a loop"); diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotAMD64Backend.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotAMD64Backend.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotAMD64Backend.java Thu Sep 13 10:45:17 2012 +0200 @@ -110,7 +110,7 @@ @Override public void visitExceptionObject(ExceptionObjectNode x) { HotSpotVMConfig config = ((HotSpotRuntime) runtime).config; - RegisterValue thread = r15.asValue(); + RegisterValue thread = config.threadRegister.asValue(); Address exceptionAddress = new Address(Kind.Object, thread, config.threadExceptionOopOffset); Address pcAddress = new Address(Kind.Long, thread, config.threadExceptionPcOffset); Value exception = emitLoad(exceptionAddress, false); diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotXirGenerator.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotXirGenerator.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/HotSpotXirGenerator.java Thu Sep 13 10:45:17 2012 +0200 @@ -284,7 +284,7 @@ asm.pload(Kind.Int, temp2i, hub, asm.i(config.klassStateOffset), false); asm.jneq(tlabFull, temp2i, asm.i(config.klassStateFullyInitialized)); - XirOperand thread = asm.createRegisterTemp("thread", target.wordKind, AMD64.r15); + XirOperand thread = asm.createRegisterTemp("thread", target.wordKind, config.threadRegister); asm.pload(target.wordKind, result, thread, asm.i(config.threadTlabTopOffset), false); asm.add(temp1, result, wordConst(asm, size)); asm.pload(target.wordKind, temp2, thread, asm.i(config.threadTlabEndOffset), false); @@ -294,7 +294,7 @@ asm.bindInline(resume); - asm.pload(target.wordKind, temp1, hub, asm.i(config.instanceHeaderPrototypeOffset), false); + asm.pload(target.wordKind, temp1, hub, asm.i(config.initialMarkWordOffset), false); asm.pstore(target.wordKind, result, asm.i(config.markOffset), temp1, false); asm.mov(temp1o, hub); // need a temporary register since Intel cannot store 64-bit constants to memory asm.pstore(Kind.Object, result, asm.i(config.hubOffset), temp1o, false); @@ -372,7 +372,7 @@ asm.and(size, size, asm.i((int) mask)); // Try tlab allocation - XirOperand thread = asm.createRegisterTemp("thread", target.wordKind, AMD64.r15); + XirOperand thread = asm.createRegisterTemp("thread", target.wordKind, config.threadRegister); asm.pload(target.wordKind, result, thread, asm.i(config.threadTlabTopOffset), false); asm.add(temp1, result, size); asm.pload(target.wordKind, temp2, thread, asm.i(config.threadTlabEndOffset), false); @@ -380,7 +380,7 @@ asm.pstore(target.wordKind, thread, asm.i(config.threadTlabTopOffset), temp1, false); // Now the new object is in result, store mark word and klass - asm.pload(target.wordKind, temp1, hub, asm.i(config.instanceHeaderPrototypeOffset), false); + asm.pload(target.wordKind, temp1, hub, asm.i(config.initialMarkWordOffset), false); asm.pstore(target.wordKind, result, asm.i(config.markOffset), temp1, false); asm.mov(temp1o, hub); // need a temporary register since Intel cannot store 64-bit constants to memory asm.pstore(Kind.Object, result, asm.i(config.hubOffset), temp1o, false); @@ -426,7 +426,7 @@ XirOperand hub = asm.createRegisterTemp("hub", Kind.Object, AMD64.rax); XirOperand rank = asm.createRegisterTemp("rank", Kind.Int, AMD64.rbx); XirOperand sizes = asm.createRegisterTemp("sizes", Kind.Long, AMD64.rcx); - XirOperand thread = asm.createRegisterTemp("thread", Kind.Long, AMD64.r15); + XirOperand thread = asm.createRegisterTemp("thread", Kind.Long, config.threadRegister); asm.add(sizes, thread, asm.l(config.threadMultiNewArrayStorageOffset)); for (int i = 0; i < dimensions; i++) { XirParameter length = asm.createInputParameter("length" + i, Kind.Int, true); diff -r ad97777056ec -r a718f153b9f2 graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/NewInstanceTest.java --- a/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/NewInstanceTest.java Wed Sep 12 13:07:35 2012 +0200 +++ b/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/NewInstanceTest.java Thu Sep 13 10:45:17 2012 +0200 @@ -38,7 +38,16 @@ Assert.assertTrue(expected != null); Assert.assertTrue(actual != null); super.assertEquals(expected.getClass(), actual.getClass()); - if (expected.getClass() != Object.class) { + + if (expected instanceof Object[]) { + Assert.assertTrue(actual instanceof Object[]); + Object[] eArr = (Object[]) expected; + Object[] aArr = (Object[]) actual; + Assert.assertTrue(eArr.length == aArr.length); + for (int i = 0; i < eArr.length; i++) { + assertEquals(eArr[i], aArr[i]); + } + } else if (expected.getClass() != Object.class) { try { expected.getClass().getDeclaredMethod("equals", Object.class); super.assertEquals(expected, actual); @@ -50,18 +59,65 @@ @Test public void test1() { test("newObject"); - test("newBigObject"); - test("newSomeObject"); - test("newEmptyString"); - test("newString", "value"); - test("newHashMap", 31); - test("newRegression", true); + } + + @Test + public void test2() { + test("newObjectTwice"); } public static Object newObject() { return new Object(); } + @Test + public void test3() { + test("newObjectLoop", 100); + } + + @Test + public void test4() { + test("newBigObject"); + } + + @Test + public void test5() { + test("newSomeObject"); + } + + @Test + public void test6() { + test("newEmptyString"); + } + + @Test + public void test7() { + test("newString", "value"); + } + + @Test + public void test8() { + test("newHashMap", 31); + } + + @Test + public void test9() { + test("newRegression", true); + } + + public static Object[] newObjectTwice() { + Object[] res = {new Object(), new Object()}; + return res; + } + + public static Object[] newObjectLoop(int n) { + Object[] res = new Object[n]; + for (int i = 0; i < n; i++) { + res[i] = new Object(); + } + return res; + } + public static BigObject newBigObject() { return new BigObject(); } diff -r ad97777056ec -r a718f153b9f2 src/share/vm/graal/graalCompiler.cpp --- a/src/share/vm/graal/graalCompiler.cpp Wed Sep 12 13:07:35 2012 +0200 +++ b/src/share/vm/graal/graalCompiler.cpp Thu Sep 13 10:45:17 2012 +0200 @@ -273,7 +273,6 @@ HotSpotResolvedJavaType::set_accessFlags(obj, klass->access_flags().as_int()); HotSpotResolvedJavaType::set_isInterface(obj, klass->is_interface()); HotSpotResolvedJavaType::set_superCheckOffset(obj, klass->super_check_offset()); - HotSpotResolvedJavaType::set_initialMarkWord(obj, (jlong) klass->prototype_header()); HotSpotResolvedJavaType::set_isInstanceClass(obj, klass->oop_is_instance()); if (klass->oop_is_javaArray()) { diff -r ad97777056ec -r a718f153b9f2 src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Wed Sep 12 13:07:35 2012 +0200 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Thu Sep 13 10:45:17 2012 +0200 @@ -551,6 +551,18 @@ } // public JavaType JavaType_superType(HotSpotResolvedType klass); +JNIEXPORT jlong JNICALL Java_com_oracle_graal_hotspot_bridge_CompilerToVMImpl_JavaType_initialMarkWord(JNIEnv *, jobject, jobject klass) { + TRACE_graal_3("CompilerToVM::JavaType_superType"); + VM_ENTRY_MARK; + KlassHandle klass_handle(java_lang_Class::as_klassOop(HotSpotResolvedJavaType::javaMirror(klass))); + if (klass_handle->oop_is_array()) { + return (int32_t)(intptr_t) markOopDesc::prototype(); + } else { + return (jlong) (intptr_t) klass_handle->prototype_header(); + } +} + +// public JavaType JavaType_superType(HotSpotResolvedType klass); JNIEXPORT jobject JNICALL Java_com_oracle_graal_hotspot_bridge_CompilerToVMImpl_JavaType_1superType(JNIEnv *, jobject, jobject klass) { TRACE_graal_3("CompilerToVM::JavaType_superType"); VM_ENTRY_MARK; @@ -707,11 +719,8 @@ BasicType basicTypes[] = { T_BOOLEAN, T_BYTE, T_SHORT, T_CHAR, T_INT, T_FLOAT, T_LONG, T_DOUBLE, T_OBJECT }; int basicTypeCount = sizeof(basicTypes) / sizeof(BasicType); -// public HotSpotVMConfig getConfiguration(); -JNIEXPORT jobject JNICALL Java_com_oracle_graal_hotspot_bridge_CompilerToVMImpl_getConfiguration(JNIEnv *env, jobject) { - jclass klass = env->FindClass("com/oracle/graal/hotspot/HotSpotVMConfig"); - assert(klass != NULL, "HotSpot vm config class not found"); - jobject config = env->AllocObject(klass); +// public void initializeConfiguration(HotSpotVMConfig config); +JNIEXPORT void JNICALL Java_com_oracle_graal_hotspot_bridge_CompilerToVMImpl_initializeConfiguration(JNIEnv *env, jobject, jobject config) { #ifdef _WIN64 set_boolean(env, config, "windowsOs", true); #else @@ -727,6 +736,7 @@ set_int(env, config, "stackShadowPages", StackShadowPages); set_int(env, config, "hubOffset", oopDesc::klass_offset_in_bytes()); set_int(env, config, "markOffset", oopDesc::mark_offset_in_bytes()); + set_int(env, config, "initialMarkWordOffset", in_bytes(Klass::prototype_header_offset())); set_int(env, config, "superCheckOffsetOffset", in_bytes(Klass::super_check_offset_offset())); set_int(env, config, "secondarySuperCacheOffset", in_bytes(Klass::secondary_super_cache_offset())); set_int(env, config, "secondarySupersOffset", in_bytes(Klass::secondary_supers_offset())); @@ -736,7 +746,6 @@ set_int(env, config, "threadTlabTopOffset", in_bytes(JavaThread::tlab_top_offset())); set_int(env, config, "threadTlabEndOffset", in_bytes(JavaThread::tlab_end_offset())); set_int(env, config, "threadObjectOffset", in_bytes(JavaThread::threadObj_offset())); - set_int(env, config, "instanceHeaderPrototypeOffset", in_bytes(Klass::prototype_header_offset())); set_int(env, config, "threadExceptionOopOffset", in_bytes(JavaThread::exception_oop_offset())); set_int(env, config, "threadExceptionPcOffset", in_bytes(JavaThread::exception_pc_offset())); set_int(env, config, "threadMultiNewArrayStorageOffset", in_bytes(JavaThread::graal_multinewarray_storage_offset())); @@ -808,7 +817,6 @@ } set_int(env, config, "arrayClassElementOffset", in_bytes(objArrayKlass::element_klass_offset())); - return config; } // public HotSpotCompiledMethod installMethod(HotSpotCompilationResult comp, boolean installCode); @@ -1040,13 +1048,14 @@ {CC"JavaType_componentType", CC"("RESOLVED_TYPE")"TYPE, FN_PTR(JavaType_1componentType)}, {CC"JavaType_uniqueConcreteSubtype", CC"("RESOLVED_TYPE")"TYPE, FN_PTR(JavaType_1uniqueConcreteSubtype)}, {CC"JavaType_superType", CC"("RESOLVED_TYPE")"TYPE, FN_PTR(JavaType_1superType)}, + {CC"JavaType_initialMarkWord", CC"("RESOLVED_TYPE")J", FN_PTR(JavaType_initialMarkWord)}, {CC"JavaType_arrayOf", CC"("RESOLVED_TYPE")"TYPE, FN_PTR(JavaType_1arrayOf)}, {CC"JavaType_fields", CC"("RESOLVED_TYPE")["RESOLVED_FIELD, FN_PTR(JavaType_1fields)}, {CC"JavaType_isInitialized", CC"("RESOLVED_TYPE")Z", FN_PTR(JavaType_1isInitialized)}, {CC"getPrimitiveArrayType", CC"("KIND")"TYPE, FN_PTR(getPrimitiveArrayType)}, {CC"getMaxCallTargetOffset", CC"("RUNTIME_CALL")J", FN_PTR(getMaxCallTargetOffset)}, {CC"getType", CC"("CLASS")"TYPE, FN_PTR(getType)}, - {CC"getConfiguration", CC"()"CONFIG, FN_PTR(getConfiguration)}, + {CC"initializeConfiguration", CC"("CONFIG")V", FN_PTR(initializeConfiguration)}, {CC"installMethod", CC"("HS_COMP_RESULT"Z"HS_CODE_INFO")"HS_COMP_METHOD, FN_PTR(installMethod)}, {CC"disassembleNative", CC"([BJ)"STRING, FN_PTR(disassembleNative)}, {CC"JavaMethod_toStackTraceElement", CC"("RESOLVED_METHOD"I)"STACK_TRACE_ELEMENT, FN_PTR(JavaMethod_1toStackTraceElement)}, diff -r ad97777056ec -r a718f153b9f2 src/share/vm/graal/graalJavaAccess.hpp --- a/src/share/vm/graal/graalJavaAccess.hpp Wed Sep 12 13:07:35 2012 +0200 +++ b/src/share/vm/graal/graalJavaAccess.hpp Thu Sep 13 10:45:17 2012 +0200 @@ -52,7 +52,6 @@ oop_field(HotSpotResolvedJavaType, javaMirror, "Ljava/lang/Class;") \ oop_field(HotSpotResolvedJavaType, simpleName, "Ljava/lang/String;") \ int_field(HotSpotResolvedJavaType, accessFlags) \ - long_field(HotSpotResolvedJavaType, initialMarkWord) \ boolean_field(HotSpotResolvedJavaType, hasFinalizer) \ boolean_field(HotSpotResolvedJavaType, hasFinalizableSubclass) \ int_field(HotSpotResolvedJavaType, superCheckOffset) \