# HG changeset patch # User Doug Simon # Date 1349612870 -7200 # Node ID ee651c726397947fc2e98190aebc83f9b97e5150 # Parent 2c913b643422f440c3a4178dba99d2561a2f5e98 split phases out of graal.phases project into graal.phases.common project diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.boot/src/com/oracle/graal/boot/meta/MethodElement.java --- a/graal/com.oracle.graal.boot/src/com/oracle/graal/boot/meta/MethodElement.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.boot/src/com/oracle/graal/boot/meta/MethodElement.java Sun Oct 07 14:27:50 2012 +0200 @@ -33,7 +33,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class MethodElement extends Element { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -30,8 +30,8 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; +import com.oracle.graal.phases.PhasePlan.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the usages of local variable "a" are replaced with the integer constant 0. Then boxing diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -25,7 +25,7 @@ import org.junit.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class CompareCanonicalizerTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompiledMethodTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompiledMethodTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompiledMethodTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -33,7 +33,7 @@ import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the usages of local variable "a" are replaced with the integer constant 0. Then diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the usages of local variable "a" are replaced with the integer constant 0. diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -28,7 +28,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class FloatingReadTest extends GraphScheduleTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -40,8 +40,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; +import com.oracle.graal.phases.PhasePlan.*; import com.oracle.graal.phases.schedule.*; /** diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfBoxingEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfBoxingEliminationTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfBoxingEliminationTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -30,8 +30,8 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; +import com.oracle.graal.phases.PhasePlan.*; +import com.oracle.graal.phases.common.*; public class IfBoxingEliminationTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -29,7 +29,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the usages of local variable "a" are replaced with the integer constant 0. diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -28,7 +28,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class InvokeExceptionTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -28,7 +28,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class InvokeHintsTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LoopUnswitchTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LoopUnswitchTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LoopUnswitchTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -28,7 +28,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.loop.phases.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class LoopUnswitchTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -33,7 +33,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the usages of local variable "a" are replaced with the integer constant 0. diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ReassociateAndCanonicalTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ReassociateAndCanonicalTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ReassociateAndCanonicalTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class ReassociateAndCanonicalTest extends GraalCompilerTest { public static int rnd = (int) (Math.random() * 100); diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ScalarTypeSystemTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ScalarTypeSystemTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ScalarTypeSystemTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * In the following tests, the scalar type system of the compiler should be complete enough to see the relation between the different conditions. diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StampCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StampCanonicalizerTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StampCanonicalizerTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.type.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; /** * This class tests some specific patterns the stamp system should be able to canonicalize away using diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public class StraighteningTest extends GraalCompilerTest { diff -r 2c913b643422 -r ee651c726397 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 Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -34,7 +34,7 @@ import com.oracle.graal.lir.cfg.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.schedule.*; /** diff -r 2c913b643422 -r ee651c726397 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 Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -32,7 +32,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; import com.oracle.graal.virtual.phases.ea.*; /** diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Sun Oct 07 14:27:50 2012 +0200 @@ -34,7 +34,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; import com.oracle.graal.virtual.nodes.*; import com.oracle.graal.virtual.phases.ea.*; diff -r 2c913b643422 -r ee651c726397 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 Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Sun Oct 07 14:27:50 2012 +0200 @@ -40,8 +40,8 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; +import com.oracle.graal.phases.PhasePlan.*; +import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.schedule.*; import com.oracle.graal.virtual.phases.ea.*; import com.oracle.graal.virtual.phases.ea.experimental.*; diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Sun Oct 07 14:27:50 2012 +0200 @@ -48,7 +48,6 @@ import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.util.*; -import com.oracle.graal.virtual.nodes.*; import com.oracle.max.asm.*; /** diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Sun Oct 07 14:27:50 2012 +0200 @@ -30,7 +30,6 @@ import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; public final class CompilationTask implements Runnable, Comparable { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompiler.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompiler.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompiler.java Sun Oct 07 14:27:50 2012 +0200 @@ -28,7 +28,6 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; /** * Calls from HotSpot into Java. diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Sun Oct 07 14:27:50 2012 +0200 @@ -38,8 +38,7 @@ import com.oracle.graal.hotspot.snippets.*; import com.oracle.graal.java.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; +import com.oracle.graal.phases.PhasePlan.*; import com.oracle.graal.snippets.*; /** diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -32,8 +32,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; -import com.oracle.graal.phases.util.*; +import com.oracle.graal.phases.common.*; public class IntrinsifyArrayCopyPhase extends Phase { private final GraalCodeCacheProvider runtime; diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java Sun Oct 07 14:27:50 2012 +0200 @@ -42,7 +42,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.util.*; +import com.oracle.graal.phases.common.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; import com.oracle.graal.snippets.Snippet.Parameter; diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderConfiguration.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderConfiguration.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderConfiguration.java Sun Oct 07 14:27:50 2012 +0200 @@ -23,7 +23,7 @@ package com.oracle.graal.java; import com.oracle.graal.api.meta.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.*; public class GraphBuilderConfiguration { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -46,7 +46,6 @@ import com.oracle.graal.nodes.type.*; import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; import com.oracle.graal.phases.util.*; /** diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopTransformations.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopTransformations.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopTransformations.java Sun Oct 07 14:27:50 2012 +0200 @@ -29,7 +29,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.common.*; public abstract class LoopTransformations { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopFullUnrollPhase.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopFullUnrollPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopFullUnrollPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,7 @@ import com.oracle.graal.loop.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.phases.*; +import com.oracle.graal.phases.*; public class LoopFullUnrollPhase extends Phase { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,6 @@ import com.oracle.graal.loop.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; public class LoopTransformHighPhase extends Phase { diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformLowPhase.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformLowPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformLowPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -26,7 +26,6 @@ import com.oracle.graal.loop.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; public class LoopTransformLowPhase extends Phase { private static final DebugMetric UNSWITCHED = Debug.metric("Unswitched"); diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/BoxingEliminationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/BoxingEliminationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,138 @@ +/* + * 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.phases.common; + +import static com.oracle.graal.graph.iterators.NodePredicates.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.iterators.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.virtual.*; +import com.oracle.graal.phases.*; + +public class BoxingEliminationPhase extends Phase { + + private int virtualIds = Integer.MIN_VALUE; + + @Override + protected void run(StructuredGraph graph) { + if (graph.getNodes(UnboxNode.class).isNotEmpty()) { + + Map phiReplacements = new HashMap<>(); + for (UnboxNode unboxNode : graph.getNodes(UnboxNode.class)) { + tryEliminate(graph, unboxNode, phiReplacements); + } + + new DeadCodeEliminationPhase().apply(graph); + + for (BoxNode boxNode : graph.getNodes(BoxNode.class)) { + tryEliminate(boxNode); + } + } + } + + private void tryEliminate(StructuredGraph graph, UnboxNode unboxNode, Map phiReplacements) { + ValueNode unboxedValue = unboxedValue(unboxNode.source(), unboxNode.destinationKind(), phiReplacements); + if (unboxedValue != null) { + assert unboxedValue.kind() == unboxNode.kind(); + unboxNode.replaceAtUsages(unboxedValue); + graph.removeFixed(unboxNode); + } + } + + private PhiNode getReplacementPhi(PhiNode phiNode, Kind kind, Map phiReplacements) { + if (!phiReplacements.containsKey(phiNode)) { + PhiNode result = null; + ObjectStamp stamp = phiNode.objectStamp(); + if (stamp.nonNull() && stamp.isExactType()) { + ResolvedJavaType type = stamp.type(); + if (type != null && type.toJava() == kind.toBoxedJavaClass()) { + StructuredGraph graph = (StructuredGraph) phiNode.graph(); + result = graph.add(new PhiNode(kind, phiNode.merge())); + phiReplacements.put(phiNode, result); + virtualizeUsages(phiNode, result, type); + int i = 0; + for (ValueNode n : phiNode.values()) { + ValueNode unboxedValue = unboxedValue(n, kind, phiReplacements); + if (unboxedValue != null) { + assert unboxedValue.kind() == kind; + result.addInput(unboxedValue); + } else { + UnboxNode unboxNode = graph.add(new UnboxNode(kind, n)); + FixedNode pred = phiNode.merge().phiPredecessorAt(i); + graph.addBeforeFixed(pred, unboxNode); + result.addInput(unboxNode); + } + ++i; + } + } + } + } + return phiReplacements.get(phiNode); + } + + private ValueNode unboxedValue(ValueNode n, Kind kind, Map phiReplacements) { + if (n instanceof BoxNode) { + BoxNode boxNode = (BoxNode) n; + return boxNode.source(); + } else if (n instanceof PhiNode) { + PhiNode phiNode = (PhiNode) n; + return getReplacementPhi(phiNode, kind, phiReplacements); + } else { + return null; + } + } + + private void tryEliminate(BoxNode boxNode) { + + assert boxNode.objectStamp().isExactType(); + virtualizeUsages(boxNode, boxNode.source(), boxNode.objectStamp().type()); + + if (boxNode.usages().filter(isNotA(VirtualState.class)).isNotEmpty()) { + // Elimination failed, because boxing object escapes. + return; + } + + FrameState stateAfter = boxNode.stateAfter(); + boxNode.setStateAfter(null); + stateAfter.safeDelete(); + + ((StructuredGraph) boxNode.graph()).removeFixed(boxNode); + } + + private void virtualizeUsages(ValueNode boxNode, ValueNode replacement, ResolvedJavaType exactType) { + ValueNode virtualValueNode = null; + VirtualObjectNode virtualObjectNode = null; + for (Node n : boxNode.usages().filter(NodePredicates.isA(VirtualState.class)).snapshot()) { + if (virtualValueNode == null) { + virtualObjectNode = n.graph().unique(new BoxedVirtualObjectNode(virtualIds++, exactType, replacement)); + } + n.replaceFirstInput(boxNode, virtualObjectNode); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,332 @@ +/* + * 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.phases.common; + +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.Graph.InputChangedListener; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + +public class CanonicalizerPhase extends Phase { + private static final int MAX_ITERATION_PER_NODE = 10; + private static final DebugMetric METRIC_CANONICALIZED_NODES = Debug.metric("CanonicalizedNodes"); + private static final DebugMetric METRIC_CANONICALIZATION_CONSIDERED_NODES = Debug.metric("CanonicalizationConsideredNodes"); + private static final DebugMetric METRIC_INFER_STAMP_CALLED = Debug.metric("InferStampCalled"); + private static final DebugMetric METRIC_STAMP_CHANGED = Debug.metric("StampChanged"); + private static final DebugMetric METRIC_SIMPLIFICATION_CONSIDERED_NODES = Debug.metric("SimplificationConsideredNodes"); + public static final DebugMetric METRIC_GLOBAL_VALUE_NUMBERING_HITS = Debug.metric("GlobalValueNumberingHits"); + + private final int newNodesMark; + private final TargetDescription target; + private final Assumptions assumptions; + private final MetaAccessProvider runtime; + private final IsImmutablePredicate immutabilityPredicate; + private final Iterable initWorkingSet; + + private NodeWorkList workList; + private Tool tool; + private List snapshotTemp; + + public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) { + this(target, runtime, assumptions, null, 0, null); + } + + /** + * @param target + * @param runtime + * @param assumptions + * @param workingSet the initial working set of nodes on which the canonicalizer works, should be an auto-grow node bitmap + * @param immutabilityPredicate + */ + public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, Iterable workingSet, IsImmutablePredicate immutabilityPredicate) { + this(target, runtime, assumptions, workingSet, 0, immutabilityPredicate); + } + + /** + * @param newNodesMark only the {@linkplain Graph#getNewNodes(int) new nodes} specified by + * this mark are processed otherwise all nodes in the graph are processed + */ + public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, int newNodesMark, IsImmutablePredicate immutabilityPredicate) { + this(target, runtime, assumptions, null, newNodesMark, immutabilityPredicate); + } + + private CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, Iterable workingSet, int newNodesMark, IsImmutablePredicate immutabilityPredicate) { + this.newNodesMark = newNodesMark; + this.target = target; + this.assumptions = assumptions; + this.runtime = runtime; + this.immutabilityPredicate = immutabilityPredicate; + this.initWorkingSet = workingSet; + this.snapshotTemp = new ArrayList<>(); + } + + @Override + protected void run(StructuredGraph graph) { + if (initWorkingSet == null) { + workList = graph.createNodeWorkList(newNodesMark == 0, MAX_ITERATION_PER_NODE); + if (newNodesMark > 0) { + workList.addAll(graph.getNewNodes(newNodesMark)); + } + } else { + workList = graph.createNodeWorkList(false, MAX_ITERATION_PER_NODE); + workList.addAll(initWorkingSet); + } + tool = new Tool(workList, runtime, target, assumptions, immutabilityPredicate); + processWorkSet(graph); + } + + public interface IsImmutablePredicate { + /** + * Determines if a given constant is an object/array whose current + * fields/elements will never change. + */ + boolean apply(Constant constant); + } + + private void processWorkSet(StructuredGraph graph) { + graph.trackInputChange(new InputChangedListener() { + @Override + public void inputChanged(Node node) { + workList.addAgain(node); + } + }); + + for (Node n : workList) { + processNode(n, graph); + } + + graph.stopTrackingInputChange(); + } + + private void processNode(Node node, StructuredGraph graph) { + if (node.isAlive()) { + METRIC_PROCESSED_NODES.increment(); + + if (tryGlobalValueNumbering(node, graph)) { + return; + } + int mark = graph.getMark(); + if (!tryKillUnused(node)) { + node.inputs().filter(GraphUtil.isFloatingNode()).snapshotTo(snapshotTemp); + if (!tryCanonicalize(node, graph, tool)) { + tryInferStamp(node, graph); + } else { + for (Node in : snapshotTemp) { + if (in.isAlive() && in.usages().isEmpty()) { + GraphUtil.killWithUnusedFloatingInputs(in); + } + } + } + snapshotTemp.clear(); + } + + for (Node newNode : graph.getNewNodes(mark)) { + workList.add(newNode); + } + } + } + + private static boolean tryKillUnused(Node node) { + if (node.isAlive() && GraphUtil.isFloatingNode().apply(node) && node.usages().isEmpty()) { + GraphUtil.killWithUnusedFloatingInputs(node); + return true; + } + return false; + } + + public static boolean tryGlobalValueNumbering(Node node, StructuredGraph graph) { + if (node.getNodeClass().valueNumberable()) { + Node newNode = graph.findDuplicate(node); + if (newNode != null) { + assert !(node instanceof FixedNode || newNode instanceof FixedNode); + node.replaceAtUsages(newNode); + node.safeDelete(); + METRIC_GLOBAL_VALUE_NUMBERING_HITS.increment(); + Debug.log("GVN applied and new node is %1s", newNode); + return true; + } + } + return false; + } + + public static boolean tryCanonicalize(final Node node, final StructuredGraph graph, final SimplifierTool tool) { + if (node instanceof Canonicalizable) { + assert !(node instanceof Simplifiable); + METRIC_CANONICALIZATION_CONSIDERED_NODES.increment(); + return Debug.scope("CanonicalizeNode", node, new Callable(){ + public Boolean call() { + ValueNode canonical = ((Canonicalizable) node).canonical(tool); +// cases: original node: +// |Floating|Fixed-unconnected|Fixed-connected| +// -------------------------------------------- +// null| 1 | X | 3 | +// -------------------------------------------- +// Floating| 2 | X | 4 | +// canonical node: -------------------------------------------- +// Fixed-unconnected| X | X | 5 | +// -------------------------------------------- +// Fixed-connected| 2 | X | 6 | +// -------------------------------------------- +// X: must not happen (checked with assertions) + if (canonical == node) { + Debug.log("Canonicalizer: work on %s", node); + return false; + } else { + Debug.log("Canonicalizer: replacing %s with %s", node, canonical); + METRIC_CANONICALIZED_NODES.increment(); + if (node instanceof FloatingNode) { + if (canonical == null) { + // case 1 + graph.removeFloating((FloatingNode) node); + } else { + // case 2 + assert !(canonical instanceof FixedNode) || (canonical.predecessor() != null || canonical instanceof StartNode) : node + " -> " + canonical + + " : replacement should be floating or fixed and connected"; + graph.replaceFloating((FloatingNode) node, canonical); + } + } else { + assert node instanceof FixedWithNextNode && node.predecessor() != null : node + " -> " + canonical + " : node should be fixed & connected (" + node.predecessor() + ")"; + if (canonical == null) { + // case 3 + graph.removeFixed((FixedWithNextNode) node); + } else if (canonical instanceof FloatingNode) { + // case 4 + graph.replaceFixedWithFloating((FixedWithNextNode) node, (FloatingNode) canonical); + } else { + assert canonical instanceof FixedNode; + if (canonical.predecessor() == null) { + assert !canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " shouldn't have successors"; + // case 5 + graph.replaceFixedWithFixed((FixedWithNextNode) node, (FixedWithNextNode) canonical); + } else { + assert canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " should have successors"; + // case 6 + node.replaceAtUsages(canonical); + graph.removeFixed((FixedWithNextNode) node); + } + } + } + return true; + } + } + }); + } else if (node instanceof Simplifiable) { + Debug.log("Canonicalizer: simplifying %s", node); + METRIC_SIMPLIFICATION_CONSIDERED_NODES.increment(); + Debug.scope("SimplifyNode", node, new Runnable() { + public void run() { + ((Simplifiable) node).simplify(tool); + } + }); + } + return node.isDeleted(); + } + + /** + * Calls {@link ValueNode#inferStamp()} on the node and, if it returns true (which means that the stamp has + * changed), re-queues the node's usages . If the stamp has changed then this method also checks if the stamp + * now describes a constant integer value, in which case the node is replaced with a constant. + */ + private void tryInferStamp(Node node, StructuredGraph graph) { + if (node.isAlive() && node instanceof ValueNode) { + ValueNode valueNode = (ValueNode) node; + METRIC_INFER_STAMP_CALLED.increment(); + if (valueNode.inferStamp()) { + METRIC_STAMP_CHANGED.increment(); + if (valueNode.stamp() instanceof IntegerStamp && valueNode.integerStamp().lowerBound() == valueNode.integerStamp().upperBound()) { + ValueNode replacement = ConstantNode.forIntegerKind(valueNode.kind(), valueNode.integerStamp().lowerBound(), graph); + Debug.log("Canonicalizer: replacing %s with %s (inferStamp)", valueNode, replacement); + valueNode.replaceAtUsages(replacement); + } else { + for (Node usage : valueNode.usages()) { + workList.addAgain(usage); + } + } + } + } + } + + private static final class Tool implements SimplifierTool { + + private final NodeWorkList nodeWorkSet; + private final MetaAccessProvider runtime; + private final TargetDescription target; + private final Assumptions assumptions; + private final IsImmutablePredicate immutabilityPredicate; + + public Tool(NodeWorkList nodeWorkSet, MetaAccessProvider runtime, TargetDescription target, Assumptions assumptions, IsImmutablePredicate immutabilityPredicate) { + this.nodeWorkSet = nodeWorkSet; + this.runtime = runtime; + this.target = target; + this.assumptions = assumptions; + this.immutabilityPredicate = immutabilityPredicate; + } + + @Override + public void deleteBranch(FixedNode branch) { + branch.predecessor().replaceFirstSuccessor(branch, null); + GraphUtil.killCFG(branch); + } + + /** + * @return the current target or {@code null} if no target is available in the current context. + */ + @Override + public TargetDescription target() { + return target; + } + + /** + * @return an object that can be used for recording assumptions or {@code null} if assumptions are not allowed in the current context. + */ + @Override + public Assumptions assumptions() { + return assumptions; + } + + @Override + public MetaAccessProvider runtime() { + return runtime; + } + + @Override + public void addToWorkList(Node node) { + nodeWorkSet.add(node); + } + + @Override + public boolean isImmutable(Constant objectConstant) { + return immutabilityPredicate != null && immutabilityPredicate.apply(objectConstant); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CheckCastEliminationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CheckCastEliminationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,384 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.graph.*; + +public class CheckCastEliminationPhase extends Phase { + + private static final DebugMetric metricInstanceOfRegistered = Debug.metric("InstanceOfRegistered"); + private static final DebugMetric metricNullCheckRegistered = Debug.metric("NullCheckRegistered"); + private static final DebugMetric metricCheckCastRemoved = Debug.metric("CheckCastRemoved"); + private static final DebugMetric metricInstanceOfRemoved = Debug.metric("InstanceOfRemoved"); + private static final DebugMetric metricNullCheckRemoved = Debug.metric("NullCheckRemoved"); + private static final DebugMetric metricNullCheckGuardRemoved = Debug.metric("NullCheckGuardRemoved"); + private static final DebugMetric metricGuardsReplaced = Debug.metric("GuardsReplaced"); + + private StructuredGraph graph; + + @Override + protected void run(StructuredGraph inputGraph) { + graph = inputGraph; + new EliminateCheckCasts(graph.start(), new State()).apply(); + } + + public static class State implements MergeableState { + + private IdentityHashMap knownTypes; + private HashSet knownNotNull; + private HashSet knownNull; + private IdentityHashMap trueConditions; + private IdentityHashMap falseConditions; + + public State() { + this.knownTypes = new IdentityHashMap<>(); + this.knownNotNull = new HashSet<>(); + this.knownNull = new HashSet<>(); + this.trueConditions = new IdentityHashMap<>(); + this.falseConditions = new IdentityHashMap<>(); + } + + public State(State other) { + this.knownTypes = new IdentityHashMap<>(other.knownTypes); + this.knownNotNull = new HashSet<>(other.knownNotNull); + this.knownNull = new HashSet<>(other.knownNull); + this.trueConditions = new IdentityHashMap<>(other.trueConditions); + this.falseConditions = new IdentityHashMap<>(other.falseConditions); + } + + @Override + public boolean merge(MergeNode merge, List withStates) { + IdentityHashMap newKnownTypes = new IdentityHashMap<>(); + HashSet newKnownNotNull = new HashSet<>(); + HashSet newKnownNull = new HashSet<>(); + IdentityHashMap newTrueConditions = new IdentityHashMap<>(); + IdentityHashMap newFalseConditions = new IdentityHashMap<>(); + + for (Map.Entry entry : knownTypes.entrySet()) { + ValueNode node = entry.getKey(); + ResolvedJavaType type = entry.getValue(); + + for (State other : withStates) { + ResolvedJavaType otherType = other.getNodeType(node); + type = widen(type, otherType); + if (type == null) { + break; + } + } + if (type == null && type != node.objectStamp().type()) { + newKnownTypes.put(node, type); + } + } + for (ValueNode node : knownNotNull) { + boolean notNull = true; + for (State other : withStates) { + if (!other.knownNotNull.contains(node)) { + notNull = false; + break; + } + } + if (notNull) { + newKnownNotNull.add(node); + } + } + for (ValueNode node : knownNull) { + boolean nul = true; + for (State other : withStates) { + if (!other.knownNull.contains(node)) { + nul = false; + break; + } + } + if (nul) { + newKnownNull.add(node); + } + } + for (Map.Entry entry : trueConditions.entrySet()) { + BooleanNode check = entry.getKey(); + ValueNode guard = entry.getValue(); + + for (State other : withStates) { + ValueNode otherGuard = other.trueConditions.get(check); + if (otherGuard == null) { + guard = null; + break; + } + if (otherGuard != guard) { + guard = merge; + } + } + if (guard != null) { + newTrueConditions.put(check, guard); + } + } + for (Map.Entry entry : falseConditions.entrySet()) { + BooleanNode check = entry.getKey(); + ValueNode guard = entry.getValue(); + + for (State other : withStates) { + ValueNode otherGuard = other.falseConditions.get(check); + if (otherGuard == null) { + guard = null; + break; + } + if (otherGuard != guard) { + guard = merge; + } + } + if (guard != null) { + newFalseConditions.put(check, guard); + } + } + + /* + // this piece of code handles phis (merges the types and knownNull/knownNotNull of the values) + if (!(merge instanceof LoopBeginNode)) { + for (PhiNode phi : merge.phis()) { + if (phi.type() == PhiType.Value && phi.kind() == Kind.Object) { + ValueNode firstValue = phi.valueAt(0); + ResolvedJavaType type = getNodeType(firstValue); + boolean notNull = knownNotNull.contains(firstValue); + boolean nul = knownNull.contains(firstValue); + + for (int i = 0; i < withStates.size(); i++) { + State otherState = withStates.get(i); + ValueNode value = phi.valueAt(i + 1); + ResolvedJavaType otherType = otherState.getNodeType(value); + type = widen(type, otherType); + notNull &= otherState.knownNotNull.contains(value); + nul &= otherState.knownNull.contains(value); + } + if (type == null && type != phi.declaredType()) { + newKnownTypes.put(phi, type); + } + if (notNull) { + newKnownNotNull.add(phi); + } + if (nul) { + newKnownNull.add(phi); + } + } + } + } + */ + this.knownTypes = newKnownTypes; + this.knownNotNull = newKnownNotNull; + this.knownNull = newKnownNull; + this.trueConditions = newTrueConditions; + this.falseConditions = newFalseConditions; + return true; + } + + public ResolvedJavaType getNodeType(ValueNode node) { + ResolvedJavaType result = knownTypes.get(node); + return result == null ? node.objectStamp().type() : result; + } + + @Override + public void loopBegin(LoopBeginNode loopBegin) { + } + + @Override + public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { + } + + @Override + public void afterSplit(FixedNode node) { + } + + @Override + public State clone() { + return new State(this); + } + } + + public static ResolvedJavaType widen(ResolvedJavaType a, ResolvedJavaType b) { + if (a == null || b == null) { + return null; + } else if (a == b) { + return a; + } else { + return a.leastCommonAncestor(b); + } + } + + public static ResolvedJavaType tighten(ResolvedJavaType a, ResolvedJavaType b) { + if (a == null) { + return b; + } else if (b == null) { + return a; + } else if (a == b) { + return a; + } else if (a.isSubtypeOf(b)) { + return a; + } else if (b.isSubtypeOf(a)) { + return b; + } else { + return a; + } + } + + public class EliminateCheckCasts extends PostOrderNodeIterator { + private BeginNode lastBegin = null; + + public EliminateCheckCasts(FixedNode start, State initialState) { + super(start, initialState); + } + + @Override + protected void node(FixedNode node) { + if (node instanceof BeginNode) { + BeginNode begin = (BeginNode) node; + lastBegin = begin; + Node pred = node.predecessor(); + if (pred != null && pred instanceof IfNode) { + IfNode ifNode = (IfNode) pred; + if (!(ifNode.compare() instanceof ConstantNode)) { + boolean isTrue = (node == ifNode.trueSuccessor()); + if (isTrue) { + state.trueConditions.put(ifNode.compare(), begin); + } else { + state.falseConditions.put(ifNode.compare(), begin); + } + } + if (ifNode.compare() instanceof InstanceOfNode) { + InstanceOfNode instanceOf = (InstanceOfNode) ifNode.compare(); + if ((node == ifNode.trueSuccessor())) { + ValueNode object = instanceOf.object(); + state.knownNotNull.add(object); + state.knownTypes.put(object, tighten(instanceOf.targetClass(), state.getNodeType(object))); + metricInstanceOfRegistered.increment(); + } + } else if (ifNode.compare() instanceof IsNullNode) { + IsNullNode nullCheck = (IsNullNode) ifNode.compare(); + boolean isNull = (node == ifNode.trueSuccessor()); + if (isNull) { + state.knownNull.add(nullCheck.object()); + } else { + state.knownNotNull.add(nullCheck.object()); + } + metricNullCheckRegistered.increment(); + } + } + for (GuardNode guard : begin.guards().snapshot()) { + BooleanNode condition = guard.condition(); + ValueNode existingGuards = guard.negated() ? state.falseConditions.get(condition) : state.trueConditions.get(condition); + if (existingGuards != null) { + guard.replaceAtUsages(existingGuards); + GraphUtil.killWithUnusedFloatingInputs(guard); + metricGuardsReplaced.increment(); + } else { + boolean removeCheck = false; + if (condition instanceof IsNullNode) { + IsNullNode isNull = (IsNullNode) condition; + if (guard.negated() && state.knownNotNull.contains(isNull.object())) { + removeCheck = true; + } else if (!guard.negated() && state.knownNull.contains(isNull.object())) { + removeCheck = true; + } + if (removeCheck) { + metricNullCheckGuardRemoved.increment(); + } + } + if (removeCheck) { + guard.replaceAtUsages(begin); + GraphUtil.killWithUnusedFloatingInputs(guard); + } else { + if (guard.negated()) { + state.falseConditions.put(condition, guard); + } else { + state.trueConditions.put(condition, guard); + } + } + } + } + } else if (node instanceof CheckCastNode) { + CheckCastNode checkCast = (CheckCastNode) node; + ResolvedJavaType type = state.getNodeType(checkCast.object()); + if (checkCast.targetClass() != null && type != null && type.isSubtypeOf(checkCast.targetClass())) { + PiNode piNode; + boolean nonNull = state.knownNotNull.contains(checkCast.object()); + piNode = graph.unique(new PiNode(checkCast.object(), lastBegin, nonNull ? StampFactory.declaredNonNull(type) : StampFactory.declared(type))); + checkCast.replaceAtUsages(piNode); + graph.removeFixed(checkCast); + metricCheckCastRemoved.increment(); + } + } else if (node instanceof IfNode) { + IfNode ifNode = (IfNode) node; + BooleanNode replaceWith = null; + BooleanNode compare = ifNode.compare(); + + if (state.trueConditions.containsKey(compare)) { + replaceWith = ConstantNode.forBoolean(true, graph); + } else if (state.falseConditions.containsKey(compare)) { + replaceWith = ConstantNode.forBoolean(false, graph); + } else { + if (compare instanceof InstanceOfNode) { + InstanceOfNode instanceOf = (InstanceOfNode) compare; + ValueNode object = instanceOf.object(); + if (state.knownNull.contains(object)) { + replaceWith = ConstantNode.forBoolean(false, graph); + } else if (state.knownNotNull.contains(object)) { + ResolvedJavaType type = state.getNodeType(object); + if (type != null && type.isSubtypeOf(instanceOf.targetClass())) { + replaceWith = ConstantNode.forBoolean(true, graph); + } + } + if (replaceWith != null) { + metricInstanceOfRemoved.increment(); + } + } else if (compare instanceof IsNullNode) { + IsNullNode isNull = (IsNullNode) compare; + ValueNode object = isNull.object(); + if (state.knownNull.contains(object)) { + replaceWith = ConstantNode.forBoolean(true, graph); + } else if (state.knownNotNull.contains(object)) { + replaceWith = ConstantNode.forBoolean(false, graph); + } + if (replaceWith != null) { + metricNullCheckRemoved.increment(); + } + } + } + if (replaceWith != null) { + ifNode.setCompare(replaceWith); + if (compare.usages().isEmpty()) { + GraphUtil.killWithUnusedFloatingInputs(compare); + } + } + } + } + } + +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ComputeProbabilityPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ComputeProbabilityPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,372 @@ +/* + * 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.phases.common; + +import java.util.*; + +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.phases.*; +import com.oracle.graal.phases.graph.*; + +public class ComputeProbabilityPhase extends Phase { + private static final double EPSILON = 1d / Integer.MAX_VALUE; + + /* + * The computation of absolute probabilities works in three steps: + * + * - The first step, "PropagateProbability", traverses the graph in post order (merges after their ends, ...) and keeps track of the "probability state". + * Whenever it encounters a ControlSplit it uses the split's probability information to divide the probability upon the successors. + * Whenever it encounters an Invoke it assumes that the exception edge is unlikely and propagates the whole probability to the normal successor. + * Whenever it encounters a Merge it sums up the probability of all predecessors. + * It also maintains a set of active loops (whose LoopBegin has been visited) and builds def/use information for the second step. + * + * - The third step propagates the loop frequencies and multiplies each FixedNode's probability with its loop frequency. + * + * TODO: add exception probability information to Invokes + */ + + @Override + protected void run(StructuredGraph graph) { + new PropagateProbability(graph.start()).apply(); + Debug.dump(graph, "After PropagateProbability"); + computeLoopFactors(); + Debug.dump(graph, "After computeLoopFactors"); + new PropagateLoopFrequency(graph.start()).apply(); + + if (GraalOptions.LoopFrequencyPropagationPolicy < 0) { + ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false); + BitSet visitedBlocks = new BitSet(cfg.getBlocks().length); + for (Loop loop : cfg.getLoops()) { + if (loop.parent == null) { + correctLoopFrequencies(loop, 1, visitedBlocks); + } + } + } + } + + private void correctLoopFrequencies(Loop loop, double parentFrequency, BitSet visitedBlocks) { + LoopBeginNode loopBegin = ((LoopBeginNode) loop.header.getBeginNode()); + double frequency = parentFrequency * loopBegin.loopFrequency(); + for (Loop child : loop.children) { + correctLoopFrequencies(child, frequency, visitedBlocks); + } + + double factor = getCorrectionFactor(loopBegin.probability(), frequency); + for (Block block : loop.blocks) { + int blockId = block.getId(); + if (!visitedBlocks.get(blockId)) { + visitedBlocks.set(blockId); + + FixedNode node = block.getBeginNode(); + while (node != block.getEndNode()) { + node.setProbability(node.probability() * factor); + node = ((FixedWithNextNode) node).next(); + } + node.setProbability(node.probability() * factor); + } + } + } + + private static double getCorrectionFactor(double probability, double frequency) { + switch (GraalOptions.LoopFrequencyPropagationPolicy) { + case -1: + return 1 / frequency; + case -2: + return (1 / frequency) * (Math.log(Math.E + frequency) - 1); + case -3: + double originalProbability = probability / frequency; + assert isRelativeProbability(originalProbability); + return (1 / frequency) * Math.max(1, Math.pow(originalProbability, 1.5) * Math.log10(frequency)); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + private void computeLoopFactors() { + for (LoopInfo info : loopInfos) { + double frequency = info.loopFrequency(); + assert frequency != -1; + } + } + + private static boolean isRelativeProbability(double prob) { + // 1.01 to allow for some rounding errors + return prob >= 0 && prob <= 1.01; + } + + public static class LoopInfo { + public final LoopBeginNode loopBegin; + + public final NodeMap> requires; + + private double loopFrequency = -1; + public boolean ended = false; + + public LoopInfo(LoopBeginNode loopBegin) { + this.loopBegin = loopBegin; + this.requires = loopBegin.graph().createNodeMap(); + } + + public double loopFrequency() { + if (loopFrequency == -1 && ended) { + double backEdgeProb = 0.0; + for (LoopEndNode le : loopBegin.loopEnds()) { + double factor = 1; + Set requireds = requires.get(le); + for (LoopInfo required : requireds) { + double t = required.loopFrequency(); + if (t == -1) { + return -1; + } + factor *= t; + } + backEdgeProb += le.probability() * factor; + } + double d = loopBegin.probability() - backEdgeProb; + if (d < EPSILON) { + d = EPSILON; + } + loopFrequency = loopBegin.probability() / d; + loopBegin.setLoopFrequency(loopFrequency); + } + return loopFrequency; + } + } + + public Set loopInfos = new HashSet<>(); + public Map> mergeLoops = new IdentityHashMap<>(); + + private class Probability implements MergeableState { + public double probability; + public HashSet loops; + public LoopInfo loopInfo; + + public Probability(double probability, HashSet loops) { + this.probability = probability; + this.loops = new HashSet<>(4); + if (loops != null) { + this.loops.addAll(loops); + } + } + + @Override + public Probability clone() { + return new Probability(probability, loops); + } + + @Override + public boolean merge(MergeNode merge, List withStates) { + if (merge.forwardEndCount() > 1) { + HashSet intersection = new HashSet<>(loops); + for (Probability other : withStates) { + intersection.retainAll(other.loops); + } + for (LoopInfo info : loops) { + if (!intersection.contains(info)) { + double loopFrequency = info.loopFrequency(); + if (loopFrequency == -1) { + return false; + } + probability *= loopFrequency; + } + } + for (Probability other : withStates) { + double prob = other.probability; + for (LoopInfo info : other.loops) { + if (!intersection.contains(info)) { + double loopFrequency = info.loopFrequency(); + if (loopFrequency == -1) { + return false; + } + prob *= loopFrequency; + } + } + probability += prob; + } + loops = intersection; + mergeLoops.put(merge, new HashSet<>(intersection)); + assert isRelativeProbability(probability) : probability; + } + return true; + } + + @Override + public void loopBegin(LoopBeginNode loopBegin) { + loopInfo = new LoopInfo(loopBegin); + loopInfos.add(loopInfo); + loops.add(loopInfo); + } + + @Override + public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { + assert loopInfo != null; + List loopEnds = loopBegin.orderedLoopEnds(); + int i = 0; + for (Probability proba : loopEndStates) { + LoopEndNode loopEnd = loopEnds.get(i++); + Set requires = loopInfo.requires.get(loopEnd); + if (requires == null) { + requires = new HashSet<>(); + loopInfo.requires.set(loopEnd, requires); + } + for (LoopInfo innerLoop : proba.loops) { + if (innerLoop != loopInfo && !this.loops.contains(innerLoop)) { + requires.add(innerLoop); + } + } + } + loopInfo.ended = true; + } + + @Override + public void afterSplit(FixedNode node) { + assert node.predecessor() != null; + Node pred = node.predecessor(); + if (pred instanceof Invoke) { + Invoke x = (Invoke) pred; + if (x.next() != node) { + probability = 0; + } + } else { + assert pred instanceof ControlSplitNode; + ControlSplitNode x = (ControlSplitNode) pred; + double sum = 0; + for (int i = 0; i < x.blockSuccessorCount(); i++) { + if (x.blockSuccessor(i) == node) { + sum += x.probability(i); + } + } + probability *= sum; + } + } + } + + private class PropagateProbability extends PostOrderNodeIterator { + + public PropagateProbability(FixedNode start) { + super(start, new Probability(1d, null)); + } + + @Override + protected void node(FixedNode node) { + node.setProbability(state.probability); + } + } + + private class LoopCount implements MergeableState { + public double count; + + public LoopCount(double count) { + this.count = count; + } + + @Override + public LoopCount clone() { + return new LoopCount(count); + } + + @Override + public boolean merge(MergeNode merge, List withStates) { + assert merge.forwardEndCount() == withStates.size() + 1; + if (merge.forwardEndCount() > 1) { + Set loops = mergeLoops.get(merge); + assert loops != null; + double countProd = 1; + for (LoopInfo loop : loops) { + countProd *= loop.loopFrequency(); + } + count = countProd; + } + return true; + } + + @Override + public void loopBegin(LoopBeginNode loopBegin) { + count *= loopBegin.loopFrequency(); + } + + @Override + public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { + // nothing to do... + } + + @Override + public void afterSplit(FixedNode node) { + // nothing to do... + } + } + + private class PropagateLoopFrequency extends PostOrderNodeIterator { + + private final FrequencyPropagationPolicy policy; + + public PropagateLoopFrequency(FixedNode start) { + super(start, new LoopCount(1d)); + this.policy = createFrequencyPropagationPolicy(); + } + + @Override + protected void node(FixedNode node) { + node.setProbability(policy.compute(node.probability(), state.count)); + } + + } + + private static FrequencyPropagationPolicy createFrequencyPropagationPolicy() { + switch (GraalOptions.LoopFrequencyPropagationPolicy) { + case -3: + case -2: + case -1: + case 0: + return new FullFrequencyPropagation(); + case 1: + return new NoFrequencyPropagation(); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + private interface FrequencyPropagationPolicy { + + double compute(double probability, double frequency); + } + + private static class FullFrequencyPropagation implements FrequencyPropagationPolicy { + + @Override + public double compute(double probability, double frequency) { + return probability * frequency; + } + } + + private static class NoFrequencyPropagation implements FrequencyPropagationPolicy { + + @Override + public double compute(double probability, double frequency) { + return probability; + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,98 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + +public class ConvertDeoptimizeToGuardPhase extends Phase { + + private static BeginNode findBeginNode(Node startNode) { + Node n = startNode; + while (true) { + if (n instanceof BeginNode) { + return (BeginNode) n; + } else { + n = n.predecessor(); + } + } + } + + @Override + protected void run(final StructuredGraph graph) { + if (graph.getNodes(DeoptimizeNode.class).isEmpty()) { + return; + } + + for (DeoptimizeNode d : graph.getNodes(DeoptimizeNode.class)) { + visitDeoptBegin(findBeginNode(d), d, graph); + } + + new DeadCodeEliminationPhase().apply(graph); + } + + private void visitDeoptBegin(BeginNode deoptBegin, DeoptimizeNode deopt, StructuredGraph graph) { + if (deoptBegin instanceof MergeNode) { + MergeNode mergeNode = (MergeNode) deoptBegin; + Debug.log("Visiting %s followed by %s", mergeNode, deopt); + List ends = mergeNode.forwardEnds().snapshot(); + for (EndNode end : ends) { + if (!end.isDeleted()) { + BeginNode beginNode = findBeginNode(end); + if (!(beginNode instanceof MergeNode)) { + visitDeoptBegin(beginNode, deopt, graph); + } + } + } + if (mergeNode.isDeleted()) { + if (!deopt.isDeleted()) { + Debug.log("Merge deleted, deopt moved to %s", findBeginNode(deopt)); + visitDeoptBegin(findBeginNode(deopt), deopt, graph); + } + } + } else if (deoptBegin.predecessor() instanceof IfNode) { + IfNode ifNode = (IfNode) deoptBegin.predecessor(); + BeginNode otherBegin = ifNode.trueSuccessor(); + BooleanNode conditionNode = ifNode.compare(); + boolean negated = false; + if (deoptBegin == ifNode.trueSuccessor()) { + negated = true; + otherBegin = ifNode.falseSuccessor(); + } + BeginNode ifBlockBegin = findBeginNode(ifNode); + Debug.log("Converting %s on %-5s branch of %s to guard for remaining branch %s. IfBegin=%s", deopt, deoptBegin == ifNode.trueSuccessor() ? "true" : "false", ifNode, otherBegin, ifBlockBegin); + FixedGuardNode guard = graph.add(new FixedGuardNode(conditionNode, deopt.reason(), deopt.action(), negated, deopt.leafGraphId())); + otherBegin.replaceAtUsages(ifBlockBegin); + FixedNode next = otherBegin.next(); + otherBegin.setNext(null); + guard.setNext(next); + ifNode.replaceAtPredecessor(guard); + GraphUtil.killCFG(ifNode); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertUnreachedToGuardPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertUnreachedToGuardPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,71 @@ +/* + * 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.phases.common; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + + +public class ConvertUnreachedToGuardPhase extends Phase { + private OptimisticOptimizations opt; + + public ConvertUnreachedToGuardPhase(OptimisticOptimizations opt) { + this.opt = opt; + } + + @Override + protected void run(StructuredGraph graph) { + if (!opt.removeNeverExecutedCode()) { + return; + } + for (Node node : graph.getNodes()) { + if (node instanceof IfNode) { + IfNode ifNode = (IfNode) node; + BeginNode insertGuard = null; + BeginNode delete = null; + boolean inverted = false; + if (ifNode.probability(IfNode.TRUE_EDGE) == 0) { + insertGuard = ifNode.falseSuccessor(); + delete = ifNode.trueSuccessor(); + inverted = true; + } else if (ifNode.probability(IfNode.FALSE_EDGE) == 0) { + insertGuard = ifNode.trueSuccessor(); + delete = ifNode.falseSuccessor(); + } + if (insertGuard != null) { + GuardNode guard = graph.unique(new GuardNode(ifNode.compare(), BeginNode.prevBegin(ifNode), DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, inverted, ifNode.leafGraphId())); + graph.addBeforeFixed(ifNode, graph.add(new ValueAnchorNode(guard))); + GraphUtil.killCFG(delete); + graph.removeSplit(ifNode, inverted ? IfNode.FALSE_EDGE : IfNode.TRUE_EDGE); + } + } + } + + } + +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CullFrameStatesPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CullFrameStatesPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,116 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.debug.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.graph.*; + +/** + * This phase culls unused FrameStates from the graph. + * It does a post order iteration over the graph, and + */ +public class CullFrameStatesPhase extends Phase { + + private static final DebugMetric metricFrameStatesCulled = Debug.metric("FrameStatesCulled"); + private static final DebugMetric metricMergesTraversed = Debug.metric("MergesTraversed"); + + @Override + protected void run(StructuredGraph graph) { + new CullFrameStates(graph.start(), new State(null)).apply(); + } + + public static class State implements MergeableState { + + private FrameState lastFrameState; + + public State(FrameState lastFrameState) { + this.lastFrameState = lastFrameState; + } + + @Override + public boolean merge(MergeNode merge, List withStates) { + FrameState stateAfter = merge.stateAfter(); + if (merge instanceof LoopBeginNode) { + if (stateAfter != null) { + lastFrameState = stateAfter; + } + return true; + } + metricMergesTraversed.increment(); + if (stateAfter != null) { + for (State other : withStates) { + if (other.lastFrameState != lastFrameState) { + lastFrameState = stateAfter; + return true; + } + } + metricFrameStatesCulled.increment(); + merge.setStateAfter(null); + if (stateAfter.usages().isEmpty()) { + GraphUtil.killWithUnusedFloatingInputs(stateAfter); + } + } + return true; + } + + @Override + public void loopBegin(LoopBeginNode loopBegin) { + } + + @Override + public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { + } + + @Override + public void afterSplit(FixedNode node) { + } + + @Override + public State clone() { + return new State(lastFrameState); + } + } + + public static class CullFrameStates extends PostOrderNodeIterator { + + public CullFrameStates(FixedNode start, State initialState) { + super(start, initialState); + } + + @Override + protected void node(FixedNode node) { + if (node instanceof StateSplit) { + FrameState stateAfter = ((StateSplit) node).stateAfter(); + if (stateAfter != null) { + state.lastFrameState = stateAfter; + } + } + } + } + +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,132 @@ +/* + * 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.phases.common; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; + + +public class DeadCodeEliminationPhase extends Phase { + + // Metrics + private static final DebugMetric metricNodesRemoved = Debug.metric("NodesRemoved"); + + private NodeFlood flood; + + @Override + protected void run(StructuredGraph graph) { + this.flood = graph.createNodeFlood(); + + flood.add(graph.start()); + iterateSuccessors(); + disconnectCFGNodes(graph); + iterateInputs(graph); + deleteNodes(graph); + + // remove chained Merges + for (MergeNode merge : graph.getNodes(MergeNode.class)) { + if (merge.forwardEndCount() == 1 && !(merge instanceof LoopBeginNode)) { + graph.reduceTrivialMerge(merge); + } + } + } + + private void iterateSuccessors() { + for (Node current : flood) { + if (current instanceof EndNode) { + EndNode end = (EndNode) current; + flood.add(end.merge()); + } else { + for (Node successor : current.successors()) { + flood.add(successor); + } + } + } + } + + private void disconnectCFGNodes(StructuredGraph graph) { + for (EndNode node : graph.getNodes(EndNode.class)) { + if (!flood.isMarked(node)) { + MergeNode merge = node.merge(); + if (merge != null && flood.isMarked(merge)) { + // We are a dead end node leading to a live merge. + merge.removeEnd(node); + } + } + } + for (LoopBeginNode loop : graph.getNodes(LoopBeginNode.class)) { + if (flood.isMarked(loop)) { + boolean reachable = false; + for (LoopEndNode end : loop.loopEnds()) { + if (flood.isMarked(end)) { + reachable = true; + break; + } + } + if (!reachable) { + Debug.log("Removing loop with unreachable end: %s", loop); + for (LoopEndNode end : loop.loopEnds().snapshot()) { + loop.removeEnd(end); + } + graph.reduceDegenerateLoopBegin(loop); + } + } + } + } + + private void deleteNodes(StructuredGraph graph) { + for (Node node : graph.getNodes()) { + if (!flood.isMarked(node)) { + node.clearInputs(); + node.clearSuccessors(); + } + } + for (Node node : graph.getNodes()) { + if (!flood.isMarked(node)) { + metricNodesRemoved.increment(); + node.safeDelete(); + } + } + } + + private void iterateInputs(StructuredGraph graph) { + for (Node node : graph.getNodes()) { + if (node instanceof LocalNode) { + flood.add(node); + } + if (flood.isMarked(node)) { + for (Node input : node.inputs()) { + flood.add(input); + } + } + } + for (Node current : flood) { + for (Node input : current.inputs()) { + flood.add(input); + } + } + } + +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ExpandBoxingNodesPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ExpandBoxingNodesPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,47 @@ +/* + * 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.phases.common; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.phases.*; + +public class ExpandBoxingNodesPhase extends Phase { + + private final BoxingMethodPool pool; + + public ExpandBoxingNodesPhase(BoxingMethodPool pool) { + this.pool = pool; + } + + @Override + protected void run(StructuredGraph graph) { + for (BoxNode boxNode : graph.getNodes(BoxNode.class)) { + boxNode.expand(pool); + } + + for (UnboxNode unboxNode : graph.getNodes(UnboxNode.class)) { + unboxNode.expand(pool); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,295 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.PhiNode.PhiType; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.graph.*; + +public class FloatingReadPhase extends Phase { + + private IdentityHashMap> loopEndStatesMap; + + private static class LoopState { + public LoopBeginNode loopBegin; + public MemoryMap state; + public IdentityHashMap loopPhiLocations = new IdentityHashMap<>(); + public ValueNode loopEntryAnyLocation; + public LoopState(LoopBeginNode loopBegin, MemoryMap state, ValueNode loopEntryAnyLocation) { + this.loopBegin = loopBegin; + this.state = state; + this.loopEntryAnyLocation = loopEntryAnyLocation; + } + + @Override + public String toString() { + return "State@" + loopBegin; + } + } + + private class MemoryMap implements MergeableState { + private IdentityHashMap lastMemorySnapshot; + private LinkedList loops; + + public MemoryMap(MemoryMap memoryMap) { + lastMemorySnapshot = new IdentityHashMap<>(memoryMap.lastMemorySnapshot); + loops = new LinkedList<>(memoryMap.loops); + } + + public MemoryMap() { + lastMemorySnapshot = new IdentityHashMap<>(); + loops = new LinkedList<>(); + } + + @Override + public String toString() { + return "Map=" + lastMemorySnapshot.toString() + " Loops=" + loops.toString(); + } + + @Override + public boolean merge(MergeNode merge, List withStates) { + if (withStates.size() == 0) { + return true; + } + + int minLoops = loops.size(); + for (MemoryMap other : withStates) { + int otherLoops = other.loops.size(); + if (otherLoops < minLoops) { + minLoops = otherLoops; + } + } + while (loops.size() > minLoops) { + loops.pop(); + } + for (MemoryMap other : withStates) { + while (other.loops.size() > minLoops) { + other.loops.pop(); + } + } + + Set keys = new HashSet<>(); + for (Object key : lastMemorySnapshot.keySet()) { + keys.add(key); + } + for (MemoryMap other : withStates) { + assert other.loops.size() == loops.size(); + assert other.loops.size() < 1 || other.loops.peek().loopBegin == loops.peek().loopBegin; + for (Object key : other.lastMemorySnapshot.keySet()) { + keys.add(key); + } + } + @SuppressWarnings("unchecked") + IdentityHashMap newMemorySnapshot = (IdentityHashMap) lastMemorySnapshot.clone(); + + for (Object key : keys) { + ValueNode merged = lastMemorySnapshot.get(key); + if (merged == null) { + merged = lastMemorySnapshot.get(LocationNode.ANY_LOCATION); + } + int mergedStatesCount = 1; + boolean isPhi = false; + for (MemoryMap other : withStates) { + ValueNode otherValue = other.lastMemorySnapshot.get(key); + if (otherValue == null) { + otherValue = other.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); + } + if (isPhi) { + ((PhiNode) merged).addInput(otherValue); + } else if (merged != otherValue) { + PhiNode phi = merge.graph().add(new PhiNode(PhiType.Memory, merge)); + for (int j = 0; j < mergedStatesCount; j++) { + phi.addInput(merged); + } + phi.addInput(otherValue); + merged = phi; + isPhi = true; + newMemorySnapshot.put(key, phi); + } + mergedStatesCount++; + } + } + + lastMemorySnapshot = newMemorySnapshot; + return true; + } + + @Override + public void loopBegin(LoopBeginNode loopBegin) { + LoopState loopState = new LoopState(loopBegin, this, lastMemorySnapshot.get(LocationNode.ANY_LOCATION)); + for (Map.Entry entry : lastMemorySnapshot.entrySet()) { + PhiNode phi = loopBegin.graph().add(new PhiNode(PhiType.Memory, loopBegin)); + phi.addInput(entry.getValue()); + entry.setValue(phi); + loopState.loopPhiLocations.put(phi, entry.getKey()); + } + loops.push(loopState); + } + + @Override + public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { + loopEndStatesMap.put(loopBegin, loopEndStates); + tryFinishLoopPhis(this, loopBegin); + } + + @Override + public void afterSplit(FixedNode node) { + // nothing + } + + @Override + public MemoryMap clone() { + return new MemoryMap(this); + } + } + + @Override + protected void run(StructuredGraph graph) { + loopEndStatesMap = new IdentityHashMap<>(); + new PostOrderNodeIterator(graph.start(), new MemoryMap()) { + @Override + protected void node(FixedNode node) { + processNode(node, state); + } + }.apply(); + } + + private void processNode(FixedNode node, MemoryMap state) { + if (node instanceof ReadNode) { + processRead((ReadNode) node, state); + } else if (node instanceof WriteNode) { + processWrite((WriteNode) node, state); + } else if (node instanceof MemoryCheckpoint) { + processCheckpoint((MemoryCheckpoint) node, state); + } else if (node instanceof LoopExitNode) { + processLoopExit((LoopExitNode) node, state); + } + } + + private static void processCheckpoint(MemoryCheckpoint checkpoint, MemoryMap state) { + processAnyLocationWrite((ValueNode) checkpoint, state); + } + + private static void processWrite(WriteNode writeNode, MemoryMap state) { + if (writeNode.location().locationIdentity() == LocationNode.ANY_LOCATION) { + processAnyLocationWrite(writeNode, state); + } + state.lastMemorySnapshot.put(writeNode.location().locationIdentity(), writeNode); + } + + private static void processAnyLocationWrite(ValueNode modifiying, MemoryMap state) { + for (Map.Entry entry : state.lastMemorySnapshot.entrySet()) { + entry.setValue(modifiying); + } + state.lastMemorySnapshot.put(LocationNode.ANY_LOCATION, modifiying); + state.loops.clear(); + } + + private void processRead(ReadNode readNode, MemoryMap state) { + StructuredGraph graph = (StructuredGraph) readNode.graph(); + assert readNode.getNullCheck() == false; + Object locationIdentity = readNode.location().locationIdentity(); + ValueNode lastLocationAccess = getLastLocationAccessForRead(state, locationIdentity); + FloatingReadNode floatingRead = graph.unique(new FloatingReadNode(readNode.object(), readNode.location(), lastLocationAccess, readNode.stamp(), readNode.dependencies())); + floatingRead.setNullCheck(readNode.getNullCheck()); + ValueAnchorNode anchor = null; + for (GuardNode guard : readNode.dependencies().filter(GuardNode.class)) { + if (anchor == null) { + anchor = graph.add(new ValueAnchorNode()); + } + anchor.addAnchoredNode(guard); + } + if (anchor != null) { + graph.addAfterFixed(readNode, anchor); + } + graph.replaceFixedWithFloating(readNode, floatingRead); + } + + private ValueNode getLastLocationAccessForRead(MemoryMap state, Object locationIdentity) { + ValueNode lastLocationAccess; + if (locationIdentity == LocationNode.FINAL_LOCATION) { + lastLocationAccess = null; + } else { + lastLocationAccess = state.lastMemorySnapshot.get(locationIdentity); + if (lastLocationAccess == null) { + LoopState lastLoop = state.loops.peek(); + if (lastLoop == null) { + lastLocationAccess = state.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); + } else { + ValueNode phiInit; + if (state.loops.size() > 1) { + phiInit = getLastLocationAccessForRead(state.loops.get(1).state, locationIdentity); + } else { + phiInit = lastLoop.loopEntryAnyLocation; + } + PhiNode phi = lastLoop.loopBegin.graph().add(new PhiNode(PhiType.Memory, lastLoop.loopBegin)); + phi.addInput(phiInit); + lastLoop.state.lastMemorySnapshot.put(locationIdentity, phi); + lastLoop.loopPhiLocations.put(phi, locationIdentity); + tryFinishLoopPhis(lastLoop.state, lastLoop.loopBegin); + lastLocationAccess = phi; + } + state.lastMemorySnapshot.put(locationIdentity, lastLocationAccess); + } + } + return lastLocationAccess; + } + + private static void processLoopExit(LoopExitNode exit, MemoryMap state) { + for (Map.Entry entry : state.lastMemorySnapshot.entrySet()) { + entry.setValue(exit.graph().unique(new ValueProxyNode(entry.getValue(), exit, PhiType.Memory))); + } + if (!state.loops.isEmpty()) { + state.loops.pop(); + } + } + + private void tryFinishLoopPhis(MemoryMap loopMemory, LoopBeginNode loopBegin) { + List loopEndStates = loopEndStatesMap.get(loopBegin); + if (loopEndStates == null) { + return; + } + LoopState loopState = loopMemory.loops.get(0); + int i = 0; + while (loopState.loopBegin != loopBegin) { + loopState = loopMemory.loops.get(++i); + } + for (PhiNode phi : loopBegin.phis()) { + if (phi.type() == PhiType.Memory && phi.valueCount() == 1) { + Object location = loopState.loopPhiLocations.get(phi); + assert location != null : "unknown location for " + phi; + for (MemoryMap endState : loopEndStates) { + ValueNode otherNode = endState.lastMemorySnapshot.get(location); + if (otherNode == null) { + otherNode = endState.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); + } + phi.addInput(otherNode); + } + } + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GlobalValueNumberingPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GlobalValueNumberingPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,60 @@ +/* + * 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.phases.common; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; + +public class GlobalValueNumberingPhase extends Phase { + + public static final DebugMetric metricGlobalValueNumberingHits = Debug.metric("GlobalValueNumberingHits"); + + @Override + protected void run(StructuredGraph graph) { + NodeBitMap visited = graph.createNodeBitMap(); + for (Node n : graph.getNodes()) { + apply(n, visited, graph); + } + } + + private void apply(Node n, NodeBitMap visited, StructuredGraph compilerGraph) { + if (!visited.isMarked(n)) { + visited.mark(n); + for (Node input : n.inputs()) { + apply(input, visited, compilerGraph); + } + if (n.getNodeClass().valueNumberable()) { + Node newNode = compilerGraph.findDuplicate(n); + if (newNode != null) { + assert !(n instanceof FixedNode || newNode instanceof FixedNode); + n.replaceAtUsages(newNode); + n.safeDelete(); + metricGlobalValueNumberingHits.increment(); + Debug.log("GVN applied and new node is %1s", newNode); + } + } + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IdentifyBoxingPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IdentifyBoxingPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,77 @@ +/* + * 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.phases.common; + +import java.lang.reflect.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.phases.*; + +public class IdentifyBoxingPhase extends Phase { + + private final BoxingMethodPool pool; + + public IdentifyBoxingPhase(BoxingMethodPool pool) { + this.pool = pool; + } + + @Override + protected void run(StructuredGraph graph) { + for (Invoke invoke : graph.getInvokes()) { + tryIntrinsify(invoke); + } + } + + public void tryIntrinsify(Invoke invoke) { + if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + return; + } + MethodCallTargetNode callTarget = invoke.methodCallTarget(); + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + if (pool.isSpecialMethod(targetMethod)) { + assert callTarget.arguments().size() == 1 : "boxing/unboxing method must have exactly one argument"; + Kind returnKind = callTarget.returnKind(); + ValueNode sourceValue = callTarget.arguments().get(0); + + // Check whether this is a boxing or an unboxing. + Node newNode = null; + if (returnKind == Kind.Object) { + // We have a boxing method here. + assert Modifier.isStatic(targetMethod.accessFlags()) : "boxing method must be static"; + Kind sourceKind = targetMethod.signature().argumentKindAt(0); + newNode = invoke.graph().add(new BoxNode(sourceValue, targetMethod.holder(), sourceKind, invoke.bci())); + } else { + // We have an unboxing method here. + assert !Modifier.isStatic(targetMethod.accessFlags()) : "unboxing method must be an instance method"; + newNode = invoke.graph().add(new UnboxNode(returnKind, sourceValue)); + } + + // Intrinsify the invoke to the special node. + invoke.intrinsify(newNode); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,420 @@ +/* + * 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.phases.common; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.internal.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.PhasePlan.*; +import com.oracle.graal.phases.common.InliningUtil.*; +import com.oracle.graal.phases.util.*; + + +public class InliningPhase extends Phase implements InliningCallback { + /* + * - Detect method which only call another method with some parameters set to constants: void foo(a) -> void foo(a, b) -> void foo(a, b, c) ... + * These should not be taken into account when determining inlining depth. + * - honor the result of overrideInliningDecision(0, caller, invoke.bci, method, true); + */ + + private final TargetDescription target; + private final GraalCodeCacheProvider runtime; + + private final Collection hints; + + private final PriorityQueue inlineCandidates = new PriorityQueue<>(); + private Assumptions assumptions; + + private final PhasePlan plan; + private final GraphCache cache; + private final WeightComputationPolicy weightComputationPolicy; + private final InliningPolicy inliningPolicy; + private final OptimisticOptimizations optimisticOpts; + + // Metrics + private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); + private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); + private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); + + public InliningPhase(TargetDescription target, GraalCodeCacheProvider runtime, Collection hints, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) { + this.target = target; + this.runtime = runtime; + this.hints = hints; + this.assumptions = assumptions; + this.cache = cache; + this.plan = plan; + this.optimisticOpts = optimisticOpts; + this.weightComputationPolicy = createWeightComputationPolicy(); + this.inliningPolicy = createInliningPolicy(); + } + + @SuppressWarnings("unchecked") + @Override + protected void run(final StructuredGraph graph) { + graph.createNodeMap(); + + if (hints != null) { + scanInvokes((Iterable) Util.uncheckedCast(this.hints)); + } else { + scanInvokes(graph.getNodes(InvokeNode.class)); + scanInvokes(graph.getNodes(InvokeWithExceptionNode.class)); + } + + while (!inlineCandidates.isEmpty() && graph.getNodeCount() < GraalOptions.MaximumDesiredSize) { + InlineInfo candidate = inlineCandidates.remove(); + if (!candidate.invoke.node().isAlive()) { + continue; + } + // refresh infos + final InlineInfo info = InliningUtil.getInlineInfo(candidate.invoke, candidate.level, runtime, assumptions, this, optimisticOpts); + + boolean inline = Debug.scope("InliningDecisions", new Callable() { + @Override + public Boolean call() throws Exception { + return info != null && inliningPolicy.isWorthInlining(graph, info); + } + }); + + if (inline) { + int mark = graph.getMark(); + Iterable newNodes = null; + try { + info.inline(graph, runtime, this); + Debug.dump(graph, "after %s", info); + newNodes = graph.getNewNodes(mark); + if (GraalOptions.OptCanonicalizer) { + new CanonicalizerPhase(target, runtime, assumptions, mark, null).apply(graph); + } +// if (GraalOptions.Intrinsify) { +// new IntrinsificationPhase(runtime).apply(graph); +// } + metricInliningPerformed.increment(); + } catch (BailoutException bailout) { + // TODO determine if we should really bail out of the whole compilation. + throw bailout; + } catch (AssertionError e) { + throw new GraalInternalError(e).addContext(info.toString()); + } catch (RuntimeException e) { + throw new GraalInternalError(e).addContext(info.toString()); + } catch (GraalInternalError e) { + throw e.addContext(info.toString()); + } + + if (newNodes != null && info.level < GraalOptions.MaximumInlineLevel) { + scanInvokes(newNodes); + } + } + } + + if (GraalOptions.Debug && graph.getNodeCount() >= GraalOptions.MaximumDesiredSize) { + Debug.scope("InliningDecisions", new Runnable() { + public void run() { + for (InlineInfo info : inlineCandidates) { + Debug.log("not inlining %s because inlining cut off by MaximumDesiredSize", InliningUtil.methodName(info)); + } + } + }); + + metricInliningStoppedByMaxDesiredSize.increment(); + } + } + + private void scanInvokes(final Iterable nodes) { + Debug.scope("InliningDecisions", new Runnable() { + public void run() { + for (Node node : nodes) { + if (node != null) { + if (node instanceof Invoke) { + Invoke invoke = (Invoke) node; + scanInvoke(invoke); + } + for (Node usage : node.usages().filterInterface(Invoke.class).snapshot()) { + scanInvoke((Invoke) usage); + } + } + } + } + }); + } + + private void scanInvoke(Invoke invoke) { + InlineInfo info = InliningUtil.getInlineInfo(invoke, computeInliningLevel(invoke), runtime, assumptions, this, optimisticOpts); + if (info != null) { + metricInliningConsidered.increment(); + inlineCandidates.add(info); + } + } + + public static final Map parsedMethods = new HashMap<>(); + + + + private static final DebugMetric metricInliningRuns = Debug.metric("Runs"); + + @Override + public StructuredGraph buildGraph(final ResolvedJavaMethod method) { + metricInliningRuns.increment(); + if (GraalOptions.CacheGraphs && cache != null) { + StructuredGraph cachedGraph = cache.get(method); + if (cachedGraph != null) { + return cachedGraph; + } + } + StructuredGraph newGraph = new StructuredGraph(method); + if (plan != null) { + plan.runPhases(PhasePosition.AFTER_PARSING, newGraph); + } + assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; + + if (GraalOptions.ProbabilityAnalysis) { + new DeadCodeEliminationPhase().apply(newGraph); + new ComputeProbabilityPhase().apply(newGraph); + } + if (GraalOptions.OptCanonicalizer) { + new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph); + } + if (GraalOptions.Intrinsify) { + new IntrinsificationPhase(runtime).apply(newGraph); + } + if (GraalOptions.CullFrameStates) { + new CullFrameStatesPhase().apply(newGraph); + } + if (GraalOptions.CacheGraphs && cache != null) { + cache.put(newGraph); + } + return newGraph; + } + + @Override + public double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke) { + boolean preferred = hints != null && hints.contains(invoke); + return weightComputationPolicy.computeWeight(caller, method, invoke, preferred); + } + + public static int graphComplexity(StructuredGraph graph) { + int result = 0; + for (Node node : graph.getNodes()) { + if (node instanceof ConstantNode || node instanceof LocalNode || node instanceof BeginNode || node instanceof ReturnNode || node instanceof UnwindNode) { + result += 0; + } else if (node instanceof PhiNode) { + result += 5; + } else if (node instanceof MergeNode || node instanceof Invoke || node instanceof LoopEndNode || node instanceof EndNode) { + result += 0; + } else if (node instanceof ControlSplitNode) { + result += ((ControlSplitNode) node).blockSuccessorCount(); + } else { + result += 1; + } + } + return Math.max(1, result); + } + + + @Override + public void recordConcreteMethodAssumption(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) { + assumptions.recordConcreteMethod(method, context, impl); + } + + @Override + public void recordMethodContentsAssumption(ResolvedJavaMethod method) { + if (assumptions != null) { + assumptions.recordMethodContents(method); + } + } + + private static int computeInliningLevel(Invoke invoke) { + int count = -1; + FrameState curState = invoke.stateAfter(); + while (curState != null) { + count++; + curState = curState.outerFrameState(); + } + return count; + } + + private static InliningPolicy createInliningPolicy() { + switch(GraalOptions.InliningPolicy) { + case 0: return new WeightBasedInliningPolicy(); + case 1: return new C1StaticSizeBasedInliningPolicy(); + case 2: return new MinimumCodeSizeBasedInliningPolicy(); + case 3: return new DynamicSizeBasedInliningPolicy(); + case 4: return new GreedySizeBasedInliningPolicy(); + default: + GraalInternalError.shouldNotReachHere(); + return null; + } + } + + private static WeightComputationPolicy createWeightComputationPolicy() { + switch(GraalOptions.WeightComputationPolicy) { + case 0: throw new GraalInternalError("removed because of invokation counter changes"); + case 1: return new BytecodeSizeBasedWeightComputationPolicy(); + case 2: return new ComplexityBasedWeightComputationPolicy(); + default: + GraalInternalError.shouldNotReachHere(); + return null; + } + } + + private interface InliningPolicy { + boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info); + } + + private static class WeightBasedInliningPolicy implements InliningPolicy { + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + if (!checkCompiledCodeSize(info)) { + return false; + } + + double penalty = Math.pow(GraalOptions.InliningSizePenaltyExp, callerGraph.getNodeCount() / (double) GraalOptions.MaximumDesiredSize) / GraalOptions.InliningSizePenaltyExp; + if (info.weight > GraalOptions.MaximumInlineWeight / (1 + penalty * GraalOptions.InliningSizePenalty)) { + Debug.log("not inlining %s (cut off by weight %e)", InliningUtil.methodName(info), info.weight); + return false; + } + + Debug.log("inlining %s (weight %f): %s", InliningUtil.methodName(info), info.weight); + return true; + } + } + + private static class C1StaticSizeBasedInliningPolicy implements InliningPolicy { + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + double maxSize = Math.max(GraalOptions.MaximumTrivialSize, Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize); + return decideSizeBasedInlining(info, maxSize); + } + } + + private static class MinimumCodeSizeBasedInliningPolicy implements InliningPolicy { + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + assert GraalOptions.ProbabilityAnalysis; + if (!checkCompiledCodeSize(info)) { + return false; + } + + double inlineWeight = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()); + double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize * inlineWeight; + maxSize = Math.max(GraalOptions.MaximumTrivialSize, maxSize); + + return decideSizeBasedInlining(info, maxSize); + } + } + + private static class DynamicSizeBasedInliningPolicy implements InliningPolicy { + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + assert GraalOptions.ProbabilityAnalysis; + if (!checkCompiledCodeSize(info)) { + return false; + } + + double inlineBoost = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()) + Math.log10(Math.max(1, info.invoke.probability() - GraalOptions.ProbabilityCapForInlining + 1)); + double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize; + maxSize = maxSize + maxSize * inlineBoost; + maxSize = Math.min(GraalOptions.MaximumGreedyInlineSize, Math.max(GraalOptions.MaximumTrivialSize, maxSize)); + + return decideSizeBasedInlining(info, maxSize); + } + } + + private static class GreedySizeBasedInliningPolicy implements InliningPolicy { + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + assert GraalOptions.ProbabilityAnalysis; + if (!checkCompiledCodeSize(info)) { + return false; + } + + double maxSize = GraalOptions.MaximumGreedyInlineSize; + if (GraalOptions.InliningBonusPerTransferredValue != 0) { + Signature signature = info.invoke.methodCallTarget().targetMethod().signature(); + int transferredValues = signature.argumentCount(!Modifier.isStatic(info.invoke.methodCallTarget().targetMethod().accessFlags())); + if (signature.returnKind() != Kind.Void) { + transferredValues++; + } + maxSize += transferredValues * GraalOptions.InliningBonusPerTransferredValue; + } + + double inlineRatio = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()); + maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * maxSize * inlineRatio; + maxSize = Math.max(maxSize, GraalOptions.MaximumTrivialSize); + + return decideSizeBasedInlining(info, maxSize); + } + } + + private static boolean decideSizeBasedInlining(InlineInfo info, double maxSize) { + boolean success = info.weight <= maxSize; + if (DebugScope.getInstance().isLogEnabled()) { + String formatterString = success ? "inlining %s (size %f <= %f)" : "not inlining %s (too large %f > %f)"; + Debug.log(formatterString, InliningUtil.methodName(info), info.weight, maxSize); + } + return success; + } + + private static boolean checkCompiledCodeSize(InlineInfo info) { + if (GraalOptions.SmallCompiledCodeSize >= 0 && info.compiledCodeSize() > GraalOptions.SmallCompiledCodeSize) { + Debug.log("not inlining %s (CompiledCodeSize %d > %d)", InliningUtil.methodName(info), info.compiledCodeSize(), GraalOptions.SmallCompiledCodeSize); + return false; + } + return true; + } + + + private interface WeightComputationPolicy { + double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke); + } + + private static class BytecodeSizeBasedWeightComputationPolicy implements WeightComputationPolicy { + @Override + public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { + double codeSize = method.codeSize(); + if (preferredInvoke) { + codeSize = codeSize / GraalOptions.BoostInliningForEscapeAnalysis; + } + return codeSize; + } + } + + private static class ComplexityBasedWeightComputationPolicy implements WeightComputationPolicy { + @Override + public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { + double complexity = method.compilationComplexity(); + if (preferredInvoke) { + complexity = complexity / GraalOptions.BoostInliningForEscapeAnalysis; + } + return complexity; + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,959 @@ +/* + * 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.phases.common; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaType.Representation; +import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.FrameState.InliningIdentifier; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + +public class InliningUtil { + + private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); + + public interface InliningCallback { + StructuredGraph buildGraph(ResolvedJavaMethod method); + double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke); + void recordMethodContentsAssumption(ResolvedJavaMethod method); + void recordConcreteMethodAssumption(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl); + } + + public static String methodName(ResolvedJavaMethod method, Invoke invoke) { + if (!Debug.isLogEnabled()) { + return null; + } else if (invoke != null && invoke.stateAfter() != null) { + return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.codeSize() + " bytes)"; + } else { + return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.codeSize() + " bytes)"; + } + } + + public static String methodName(InlineInfo info) { + if (!Debug.isLogEnabled()) { + return null; + } else if (info.invoke != null && info.invoke.stateAfter() != null) { + return methodName(info.invoke.stateAfter(), info.invoke.bci()) + ": " + info.toString(); + } else { + return info.toString(); + } + } + + private static String methodName(FrameState frameState, int bci) { + StringBuilder sb = new StringBuilder(); + if (frameState.outerFrameState() != null) { + sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); + sb.append("->"); + } + sb.append(MetaUtil.format("%h.%n", frameState.method())); + sb.append("@").append(bci); + return sb.toString(); + } + + /** + * Represents an opportunity for inlining at the given invoke, with the given weight and level. + * The weight is the amortized weight of the additional code - so smaller is better. + * The level is the number of nested inlinings that lead to this invoke. + */ + public abstract static class InlineInfo implements Comparable { + public final Invoke invoke; + public final double weight; + public final int level; + + public InlineInfo(Invoke invoke, double weight, int level) { + this.invoke = invoke; + this.weight = weight; + this.level = level; + } + + public abstract int compiledCodeSize(); + + @Override + public int compareTo(InlineInfo o) { + return (weight < o.weight) ? -1 : (weight > o.weight) ? 1 : 0; + } + + protected static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) { + return Debug.scope("GetInliningGraph", concrete, new Callable() { + @Override + public StructuredGraph call() throws Exception { + return callback.buildGraph(concrete); + } + }); + } + + public abstract boolean canDeopt(); + + /** + * Performs the inlining described by this object and returns the node that represents the return value of the + * inlined method (or null for void methods and methods that have no non-exceptional exit). + * + * @param graph + * @param runtime + * @param callback + */ + public abstract void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback); + } + + /** + * Represents an inlining opportunity where the compiler can statically determine a monomorphic target method and + * therefore is able to determine the called method exactly. + */ + private static class ExactInlineInfo extends InlineInfo { + public final ResolvedJavaMethod concrete; + + public ExactInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaMethod concrete) { + super(invoke, weight, level); + this.concrete = concrete; + } + + @Override + public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, final InliningCallback callback) { + StructuredGraph graph = getGraph(concrete, callback); + assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); + callback.recordMethodContentsAssumption(concrete); + InliningUtil.inline(invoke, graph, true); + } + + @Override + public int compiledCodeSize() { + return concrete.compiledCodeSize(); + } + + @Override + public String toString() { + return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + @Override + public boolean canDeopt() { + return false; + } + } + + /** + * Represents an inlining opportunity for which profiling information suggests a monomorphic receiver, but for which + * the receiver type cannot be proven. A type check guard will be generated if this inlining is performed. + */ + private static class TypeGuardInlineInfo extends InlineInfo { + public final ResolvedJavaMethod concrete; + public final ResolvedJavaType type; + + public TypeGuardInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaMethod concrete, ResolvedJavaType type) { + super(invoke, weight, level); + this.concrete = concrete; + this.type = type; + } + + @Override + public int compiledCodeSize() { + return concrete.compiledCodeSize(); + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { + // receiver null check must be before the type check + InliningUtil.receiverNullCheck(invoke); + ValueNode receiver = invoke.methodCallTarget().receiver(); + LoadHubNode receiverHub = graph.add(new LoadHubNode(receiver)); + ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), runtime, graph); + ObjectEqualsNode typeCheck = graph.unique(new ObjectEqualsNode(receiverHub, typeHub)); + FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, invoke.leafGraphId())); + ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); + assert invoke.predecessor() != null; + + ValueNode anchoredReceiver = createAnchoredReceiver(graph, anchor, type, receiver, true); + invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + graph.addBeforeFixed(invoke.node(), receiverHub); + graph.addBeforeFixed(invoke.node(), guard); + graph.addBeforeFixed(invoke.node(), anchor); + + StructuredGraph calleeGraph = getGraph(concrete, callback); + assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); + callback.recordMethodContentsAssumption(concrete); + InliningUtil.inline(invoke, calleeGraph, false); + } + + @Override + public String toString() { + return "type-checked " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + @Override + public boolean canDeopt() { + return true; + } + } + + /** + * Polymorphic inlining of m methods with n type checks (n >= m) in case that the profiling information suggests a reasonable + * amounts of different receiver types and different methods. If an unknown type is encountered a deoptimization is triggered. + */ + private static class MultiTypeGuardInlineInfo extends InlineInfo { + public final List concretes; + public final ProfiledType[] ptypes; + public final int[] typesToConcretes; + public final double notRecordedTypeProbability; + + public MultiTypeGuardInlineInfo(Invoke invoke, double weight, int level, List concretes, ProfiledType[] ptypes, + int[] typesToConcretes, double notRecordedTypeProbability) { + super(invoke, weight, level); + assert concretes.size() > 0 && concretes.size() <= ptypes.length : "must have at least one method but no more than types methods"; + assert ptypes.length == typesToConcretes.length : "array lengths must match"; + + this.concretes = concretes; + this.ptypes = ptypes; + this.typesToConcretes = typesToConcretes; + this.notRecordedTypeProbability = notRecordedTypeProbability; + } + + @Override + public int compiledCodeSize() { + int result = 0; + for (ResolvedJavaMethod m: concretes) { + result += m.compiledCodeSize(); + } + return result; + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { + int numberOfMethods = concretes.size(); + boolean hasReturnValue = invoke.node().kind() != Kind.Void; + + // receiver null check must be the first node + InliningUtil.receiverNullCheck(invoke); + if (numberOfMethods > 1 || shouldFallbackToInvoke()) { + inlineMultipleMethods(graph, runtime, callback, numberOfMethods, hasReturnValue); + } else { + inlineSingleMethod(graph, runtime, callback); + } + } + + private boolean shouldFallbackToInvoke() { + return notRecordedTypeProbability > 0; + } + + private void inlineMultipleMethods(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, int numberOfMethods, boolean hasReturnValue) { + FixedNode continuation = invoke.next(); + + ValueNode originalReceiver = invoke.methodCallTarget().receiver(); + // setup merge and phi nodes for results and exceptions + MergeNode returnMerge = graph.add(new MergeNode()); + returnMerge.setProbability(invoke.probability()); + returnMerge.setStateAfter(invoke.stateAfter().duplicate(invoke.stateAfter().bci)); + + PhiNode returnValuePhi = null; + if (hasReturnValue) { + returnValuePhi = graph.unique(new PhiNode(invoke.node().kind(), returnMerge)); + } + + MergeNode exceptionMerge = null; + PhiNode exceptionObjectPhi = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + DispatchBeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + + exceptionMerge = graph.add(new MergeNode()); + exceptionMerge.setProbability(exceptionEdge.probability()); + + FixedNode exceptionSux = exceptionObject.next(); + graph.addBeforeFixed(exceptionSux, exceptionMerge); + exceptionObjectPhi = graph.unique(new PhiNode(Kind.Object, exceptionMerge)); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Void, exceptionObjectPhi)); + } + + // create one separate block for each invoked method + BeginNode[] calleeEntryNodes = new BeginNode[numberOfMethods]; + for (int i = 0; i < numberOfMethods; i++) { + int predecessors = 0; + double probability = 0; + for (int j = 0; j < typesToConcretes.length; j++) { + if (typesToConcretes[j] == i) { + predecessors++; + probability += ptypes[j].probability; + } + } + + calleeEntryNodes[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, predecessors, invoke.probability() * probability, true); + } + + // create the successor for an unknown type + FixedNode unknownTypeNode; + if (shouldFallbackToInvoke()) { + unknownTypeNode = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, 1, notRecordedTypeProbability, false); + } else { + unknownTypeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); + } + + // replace the invoke exception edge + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithExceptionNode.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + exceptionObject.replaceAtUsages(exceptionObjectPhi); + exceptionObject.setNext(null); + GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); + } + + // replace the invoke with a switch on the type of the actual receiver + LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver())); + graph.addBeforeFixed(invoke.node(), receiverHub); + FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, calleeEntryNodes, unknownTypeNode); + + assert invoke.next() == continuation; + invoke.setNext(null); + returnMerge.setNext(continuation); + invoke.node().replaceAtUsages(returnValuePhi); + invoke.node().replaceAndDelete(dispatchOnType); + + ArrayList replacements = new ArrayList<>(); + + // do the actual inlining for every invoke + for (int i = 0; i < calleeEntryNodes.length; i++) { + BeginNode node = calleeEntryNodes[i]; + Invoke invokeForInlining = (Invoke) node.next(); + + ResolvedJavaType commonType = getLeastCommonType(i); + ValueNode receiver = invokeForInlining.methodCallTarget().receiver(); + boolean exact = getTypeCount(i) == 1; + PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); + invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + ResolvedJavaMethod concrete = concretes.get(i); + StructuredGraph calleeGraph = getGraph(concrete, callback); + callback.recordMethodContentsAssumption(concrete); + assert !IntrinsificationPhase.canIntrinsify(invokeForInlining, concrete, runtime); + InliningUtil.inline(invokeForInlining, calleeGraph, false); + replacements.add(anchoredReceiver); + } + if (shouldFallbackToInvoke()) { + replacements.add(null); + } + if (GraalOptions.OptTailDuplication) { + /* + * We might want to perform tail duplication at the merge after a type switch, if there are invokes that would + * benefit from the improvement in type information. + */ + FixedNode current = returnMerge; + int opportunities = 0; + do { + if (current instanceof InvokeNode && ((InvokeNode) current).methodCallTarget().receiver() == originalReceiver) { + opportunities++; + } else if (current.inputs().contains(originalReceiver)) { + opportunities++; + } + current = ((FixedWithNextNode) current).next(); + } while (current instanceof FixedWithNextNode); + if (opportunities > 0) { + metricInliningTailDuplication.increment(); + Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); + TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacements); + } + } + } + + private int getTypeCount(int concreteMethodIndex) { + int count = 0; + for (int i = 0; i < typesToConcretes.length; i++) { + if (typesToConcretes[i] == concreteMethodIndex) { + count++; + } + } + return count; + } + + private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { + ResolvedJavaType commonType = null; + for (int i = 0; i < typesToConcretes.length; i++) { + if (typesToConcretes[i] == concreteMethodIndex) { + if (commonType == null) { + commonType = ptypes[i].type; + } else { + commonType = commonType.leastCommonAncestor(ptypes[i].type); + } + } + } + assert commonType != null; + return commonType; + } + + private void inlineSingleMethod(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { + assert concretes.size() == 1 && ptypes.length > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; + + MergeNode calleeEntryNode = graph.add(new MergeNode()); + calleeEntryNode.setProbability(invoke.probability()); + LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver())); + graph.addBeforeFixed(invoke.node(), receiverHub); + + FixedNode unknownTypeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); + FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, new BeginNode[] {calleeEntryNode}, unknownTypeNode); + + FixedWithNextNode pred = (FixedWithNextNode) invoke.node().predecessor(); + pred.setNext(dispatchOnType); + calleeEntryNode.setNext(invoke.node()); + + ResolvedJavaMethod concrete = concretes.get(0); + StructuredGraph calleeGraph = getGraph(concrete, callback); + assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); + callback.recordMethodContentsAssumption(concrete); + InliningUtil.inline(invoke, calleeGraph, false); + } + + private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode objectClassNode, BeginNode[] calleeEntryNodes, FixedNode unknownTypeSux) { + assert ptypes.length > 1; + + ResolvedJavaType[] types = new ResolvedJavaType[ptypes.length]; + double[] probabilities = new double[ptypes.length + 1]; + BeginNode[] successors = new BeginNode[ptypes.length + 1]; + int[] keySuccessors = new int[ptypes.length + 1]; + for (int i = 0; i < ptypes.length; i++) { + types[i] = ptypes[i].type; + probabilities[i] = ptypes[i].probability; + FixedNode entry = calleeEntryNodes[typesToConcretes[i]]; + if (entry instanceof MergeNode) { + EndNode endNode = graph.add(new EndNode()); + ((MergeNode) entry).addForwardEnd(endNode); + entry = endNode; + } + successors[i] = BeginNode.begin(entry); + keySuccessors[i] = i; + } + assert !(unknownTypeSux instanceof MergeNode); + successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); + probabilities[successors.length - 1] = notRecordedTypeProbability; + keySuccessors[successors.length - 1] = successors.length - 1; + + TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(objectClassNode, successors, probabilities, types, probabilities, keySuccessors)); + + return typeSwitch; + } + + private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, + MergeNode exceptionMerge, PhiNode exceptionObjectPhi, int predecessors, double probability, boolean useForInlining) { + Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining, probability); + BeginNode calleeEntryNode = graph.add(predecessors > 1 ? new MergeNode() : new BeginNode()); + calleeEntryNode.setNext(duplicatedInvoke.node()); + calleeEntryNode.setProbability(probability); + + EndNode endNode = graph.add(new EndNode()); + endNode.setProbability(probability); + + duplicatedInvoke.setNext(endNode); + returnMerge.addForwardEnd(endNode); + + if (returnValuePhi != null) { + returnValuePhi.addInput(duplicatedInvoke.node()); + } + return calleeEntryNode; + } + + private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining, double probability) { + Invoke result = (Invoke) invoke.node().copyWithInputs(); + Node callTarget = result.callTarget().copyWithInputs(); + result.node().replaceFirstInput(result.callTarget(), callTarget); + result.setUseForInlining(useForInlining); + result.setProbability(probability); + + Kind kind = invoke.node().kind(); + if (!kind.isVoid()) { + FrameState stateAfter = invoke.stateAfter(); + stateAfter = stateAfter.duplicate(stateAfter.bci); + stateAfter.replaceFirstInput(invoke.node(), result.node()); + result.setStateAfter(stateAfter); + } + + if (invoke instanceof InvokeWithExceptionNode) { + assert exceptionMerge != null && exceptionObjectPhi != null; + + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + FrameState stateAfterException = exceptionObject.stateAfter(); + + BeginNode newExceptionEdge = (BeginNode) exceptionEdge.copyWithInputs(); + ExceptionObjectNode newExceptionObject = (ExceptionObjectNode) exceptionObject.copyWithInputs(); + // set new state (pop old exception object, push new one) + newExceptionObject.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionObject)); + newExceptionEdge.setNext(newExceptionObject); + + EndNode endNode = graph.add(new EndNode()); + newExceptionObject.setNext(endNode); + exceptionMerge.addForwardEnd(endNode); + exceptionObjectPhi.addInput(newExceptionObject); + + ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); + builder.append(String.format(", %d methods with %d type checks:", concretes.size(), ptypes.length)); + for (int i = 0; i < concretes.size(); i++) { + builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); + } + return builder.toString(); + } + + @Override + public boolean canDeopt() { + return true; + } + } + + + /** + * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic target method, + * but for which an assumption has to be registered because of non-final classes. + */ + private static class AssumptionInlineInfo extends ExactInlineInfo { + public final ResolvedJavaType context; + + public AssumptionInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaType context, ResolvedJavaMethod concrete) { + super(invoke, weight, level, concrete); + this.context = context; + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { + if (Debug.isLogEnabled()) { + String targetName = MetaUtil.format("%H.%n(%p):%r", invoke.methodCallTarget().targetMethod()); + String concreteName = MetaUtil.format("%H.%n(%p):%r", concrete); + Debug.log("recording concrete method assumption: %s on receiver type %s -> %s", targetName, context, concreteName); + } + callback.recordConcreteMethodAssumption(invoke.methodCallTarget().targetMethod(), context, concrete); + + super.inline(graph, runtime, callback); + } + + @Override + public String toString() { + return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + @Override + public boolean canDeopt() { + return true; + } + } + + /** + * Determines if inlining is possible at the given invoke node. + * @param invoke the invoke that should be inlined + * @param level the number of nested inlinings that lead to this invoke, or 0 if the invoke was part of the initial graph + * @param runtime a GraalRuntime instance used to determine of the invoke can be inlined and/or should be intrinsified + * @param callback a callback that is used to determine the weight of a specific inlining + * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke + */ + public static InlineInfo getInlineInfo(Invoke invoke, int level, GraalCodeCacheProvider runtime, Assumptions assumptions, InliningCallback callback, OptimisticOptimizations optimisticOpts) { + if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + // The invoke has already been lowered , or has been created as a low-level node. We have no method information. + return null; + } + ResolvedJavaMethod parent = invoke.stateAfter().method(); + MethodCallTargetNode callTarget = invoke.methodCallTarget(); + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + if (targetMethod == null) { + return null; + } + if (!checkInvokeConditions(invoke)) { + return null; + } + + if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { + if (checkTargetConditions(invoke, targetMethod, optimisticOpts)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, targetMethod, invoke); + return new ExactInlineInfo(invoke, weight, level, targetMethod); + } + return null; + } + ObjectStamp receiverStamp = callTarget.receiver().objectStamp(); + ResolvedJavaType receiverType = receiverStamp.type(); + if (receiverStamp.isExactType()) { + assert receiverType.isSubtypeOf(targetMethod.holder()) : receiverType + " subtype of " + targetMethod.holder() + " for " + targetMethod; + ResolvedJavaMethod resolved = receiverType.resolveMethodImpl(targetMethod); + if (checkTargetConditions(invoke, resolved, optimisticOpts)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, resolved, invoke); + return new ExactInlineInfo(invoke, weight, level, resolved); + } + return null; + } + ResolvedJavaType holder = targetMethod.holder(); + + if (receiverStamp.type() != null) { + // the invoke target might be more specific than the holder (happens after inlining: locals lose their declared type...) + // TODO (lstadler) fix this + if (receiverType != null && receiverType.isSubtypeOf(holder)) { + holder = receiverType; + } + } + // TODO (thomaswue) fix this + if (assumptions != null) { + ResolvedJavaMethod concrete = holder.uniqueConcreteMethod(targetMethod); + if (concrete != null) { + if (checkTargetConditions(invoke, concrete, optimisticOpts)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + return new AssumptionInlineInfo(invoke, weight, level, holder, concrete); + } + return null; + } + } + + // type check based inlining + return getTypeCheckedInlineInfo(invoke, level, callback, parent, targetMethod, optimisticOpts); + } + + private static InlineInfo getTypeCheckedInlineInfo(Invoke invoke, int level, InliningCallback callback, ResolvedJavaMethod parent, ResolvedJavaMethod targetMethod, OptimisticOptimizations optimisticOpts) { + ProfilingInfo profilingInfo = parent.profilingInfo(); + JavaTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci()); + if (typeProfile != null) { + ProfiledType[] ptypes = typeProfile.getTypes(); + + if (ptypes != null && ptypes.length > 0) { + double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); + if (ptypes.length == 1 && notRecordedTypeProbability == 0) { + if (optimisticOpts.inlineMonomorphicCalls()) { + ResolvedJavaType type = ptypes[0].type; + ResolvedJavaMethod concrete = type.resolveMethodImpl(targetMethod); + if (checkTargetConditions(invoke, concrete, optimisticOpts)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + return new TypeGuardInlineInfo(invoke, weight, level, concrete, type); + } + + Debug.log("not inlining %s because method can't be inlined", methodName(targetMethod, invoke)); + return null; + } else { + Debug.log("not inlining %s because GraalOptions.InlineMonomorphicCalls == false", methodName(targetMethod, invoke)); + return null; + } + } else { + invoke.setMegamorphic(true); + if (optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0 || optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { + // TODO (chaeubl) inlining of multiple methods should work differently + // 1. check which methods can be inlined + // 2. for those methods, use weight and probability to compute which of them should be inlined + // 3. do the inlining + // a) all seen methods can be inlined -> do so and guard with deopt + // b) some methods can be inlined -> inline them and fall back to invocation if violated + // TODO (chaeubl) sort types by probability + + // determine concrete methods and map type to specific method + ArrayList concreteMethods = new ArrayList<>(); + int[] typesToConcretes = new int[ptypes.length]; + for (int i = 0; i < ptypes.length; i++) { + ResolvedJavaMethod concrete = ptypes[i].type.resolveMethodImpl(targetMethod); + + int index = concreteMethods.indexOf(concrete); + if (index < 0) { + index = concreteMethods.size(); + concreteMethods.add(concrete); + } + typesToConcretes[i] = index; + } + + double totalWeight = 0; + boolean canInline = true; + for (ResolvedJavaMethod concrete: concreteMethods) { + if (!checkTargetConditions(invoke, concrete, optimisticOpts)) { + canInline = false; + break; + } + totalWeight += callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + } + + if (canInline) { + return new MultiTypeGuardInlineInfo(invoke, totalWeight, level, concreteMethods, ptypes, typesToConcretes, notRecordedTypeProbability); + } else { + Debug.log("not inlining %s because it is a polymorphic method call and at least one invoked method cannot be inlined", methodName(targetMethod, invoke)); + return null; + } + } else { + if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { + Debug.log("not inlining %s because GraalOptions.InlinePolymorphicCalls == false", methodName(targetMethod, invoke)); + } else { + Debug.log("not inlining %s because GraalOptions.InlineMegamorphicCalls == false", methodName(targetMethod, invoke)); + } + return null; + } + } + } + + Debug.log("not inlining %s because no types/probabilities were recorded", methodName(targetMethod, invoke)); + return null; + } else { + Debug.log("not inlining %s because no type profile exists", methodName(targetMethod, invoke)); + return null; + } + } + + private static PiNode createAnchoredReceiver(StructuredGraph graph, FixedNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { + // to avoid that floating reads on receiver fields float above the type check + return graph.unique(new PiNode(receiver, anchor, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType))); + } + + private static boolean checkInvokeConditions(Invoke invoke) { + if (invoke.stateAfter() == null) { + Debug.log("not inlining %s because the invoke has no after state", methodName(invoke.methodCallTarget().targetMethod(), invoke)); + return false; + } + if (invoke.predecessor() == null) { + Debug.log("not inlining %s because the invoke is dead code", methodName(invoke.methodCallTarget().targetMethod(), invoke)); + return false; + } + if (!invoke.useForInlining()) { + Debug.log("not inlining %s because invoke is marked to be not used for inlining", methodName(invoke.methodCallTarget().targetMethod(), invoke)); + return false; + } + return true; + } + + private static boolean checkTargetConditions(Invoke invoke, JavaMethod method, OptimisticOptimizations optimisticOpts) { + if (method == null) { + Debug.log("not inlining because method is not resolved"); + return false; + } + if (!(method instanceof ResolvedJavaMethod)) { + Debug.log("not inlining %s because it is unresolved", method.toString()); + return false; + } + ResolvedJavaMethod resolvedMethod = (ResolvedJavaMethod) method; + if (Modifier.isNative(resolvedMethod.accessFlags())) { + Debug.log("not inlining %s because it is a native method", methodName(resolvedMethod, invoke)); + return false; + } + if (Modifier.isAbstract(resolvedMethod.accessFlags())) { + Debug.log("not inlining %s because it is an abstract method", methodName(resolvedMethod, invoke)); + return false; + } + if (!resolvedMethod.holder().isInitialized()) { + Debug.log("not inlining %s because of non-initialized class", methodName(resolvedMethod, invoke)); + return false; + } + if (!resolvedMethod.canBeInlined()) { + Debug.log("not inlining %s because it is marked non-inlinable", methodName(resolvedMethod, invoke)); + return false; + } + if (computeRecursiveInliningLevel(invoke.stateAfter(), (ResolvedJavaMethod) method) > GraalOptions.MaximumRecursiveInlining) { + Debug.log("not inlining %s because it exceeds the maximum recursive inlining depth", methodName(resolvedMethod, invoke)); + return false; + } + OptimisticOptimizations calleeOpts = new OptimisticOptimizations(resolvedMethod); + if (calleeOpts.lessOptimisticThan(optimisticOpts)) { + Debug.log("not inlining %s because callee uses less optimistic optimizations than caller", methodName(resolvedMethod, invoke)); + return false; + } + + return true; + } + + private static int computeRecursiveInliningLevel(FrameState state, ResolvedJavaMethod method) { + assert state != null; + + int count = 0; + FrameState curState = state; + while (curState != null) { + if (curState.method() == method) { + count++; + } + curState = curState.outerFrameState(); + } + return count; + } + + /** + * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. + * + * @param invoke the invoke that will be replaced + * @param inlineGraph the graph that the invoke will be replaced with + * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required + */ + public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { + InliningIdentifier identifier = new InliningIdentifier(inlineGraph.method(), invoke); + NodeInputList parameters = invoke.callTarget().arguments(); + StructuredGraph graph = (StructuredGraph) invoke.node().graph(); + + FrameState stateAfter = invoke.stateAfter(); + assert stateAfter.isAlive(); + + IdentityHashMap replacements = new IdentityHashMap<>(); + ArrayList nodes = new ArrayList<>(); + ReturnNode returnNode = null; + UnwindNode unwindNode = null; + StartNode entryPointNode = inlineGraph.start(); + FixedNode firstCFGNode = entryPointNode.next(); + for (Node node : inlineGraph.getNodes()) { + if (node == entryPointNode || node == entryPointNode.stateAfter()) { + // Do nothing. + } else if (node instanceof LocalNode) { + replacements.put(node, parameters.get(((LocalNode) node).index())); + } else { + nodes.add(node); + if (node instanceof ReturnNode) { + assert returnNode == null; + returnNode = (ReturnNode) node; + } else if (node instanceof UnwindNode) { + assert unwindNode == null; + unwindNode = (UnwindNode) node; + } + } + } + replacements.put(entryPointNode, BeginNode.prevBegin(invoke.node())); // ensure proper anchoring of things that where anchored to the StartNode + + assert invoke.node().successors().first() != null : invoke; + assert invoke.node().predecessor() != null; + + Map duplicates = graph.addDuplicates(nodes, replacements); + FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); + if (receiverNullCheck) { + receiverNullCheck(invoke); + } + invoke.node().replaceAtPredecessor(firstCFGNodeDuplicate); + + FrameState stateAtExceptionEdge = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); + if (unwindNode != null) { + assert unwindNode.predecessor() != null; + assert invokeWithException.exceptionEdge().successors().count() == 1; + ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge().next(); + stateAtExceptionEdge = obj.stateAfter(); + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + obj.replaceAtUsages(unwindDuplicate.exception()); + unwindDuplicate.clearInputs(); + Node n = obj.next(); + obj.setNext(null); + unwindDuplicate.replaceAndDelete(n); + } else { + invokeWithException.killExceptionEdge(); + } + } else { + if (unwindNode != null) { + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + DeoptimizeNode deoptimizeNode = new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler, invoke.leafGraphId()); + unwindDuplicate.replaceAndDelete(graph.add(deoptimizeNode)); + // move the deopt upwards if there is a monitor exit that tries to use the "after exception" frame state + // (because there is no "after exception" frame state!) + if (deoptimizeNode.predecessor() instanceof MonitorExitNode) { + MonitorExitNode monitorExit = (MonitorExitNode) deoptimizeNode.predecessor(); + if (monitorExit.stateAfter() != null && monitorExit.stateAfter().bci == FrameState.AFTER_EXCEPTION_BCI) { + FrameState monitorFrameState = monitorExit.stateAfter(); + graph.removeFixed(monitorExit); + monitorFrameState.safeDelete(); + } + } + } + } + + FrameState outerFrameState = null; + double invokeProbability = invoke.node().probability(); + for (Node node : duplicates.values()) { + if (GraalOptions.ProbabilityAnalysis) { + if (node instanceof FixedNode) { + FixedNode fixed = (FixedNode) node; + double newProbability = fixed.probability() * invokeProbability; + if (GraalOptions.LimitInlinedProbability) { + newProbability = Math.min(newProbability, invokeProbability); + } + fixed.setProbability(newProbability); + } + } + if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + assert frameState.bci != FrameState.BEFORE_BCI; + if (frameState.bci == FrameState.AFTER_BCI) { + frameState.replaceAndDelete(stateAfter); + } else if (frameState.bci == FrameState.AFTER_EXCEPTION_BCI) { + if (frameState.isAlive()) { + assert stateAtExceptionEdge != null; + frameState.replaceAndDelete(stateAtExceptionEdge); + } else { + assert stateAtExceptionEdge == null; + } + } else { + // only handle the outermost frame states + if (frameState.outerFrameState() == null) { + assert frameState.method() == inlineGraph.method(); + if (outerFrameState == null) { + outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); + outerFrameState.setDuringCall(true); + } + frameState.setOuterFrameState(outerFrameState); + frameState.setInliningIdentifier(identifier); + } + } + } + } + + Node returnValue = null; + if (returnNode != null) { + if (returnNode.result() instanceof LocalNode) { + returnValue = replacements.get(returnNode.result()); + } else { + returnValue = duplicates.get(returnNode.result()); + } + invoke.node().replaceAtUsages(returnValue); + Node returnDuplicate = duplicates.get(returnNode); + returnDuplicate.clearInputs(); + Node n = invoke.next(); + invoke.setNext(null); + returnDuplicate.replaceAndDelete(n); + } + + invoke.node().clearInputs(); + invoke.node().replaceAtUsages(null); + GraphUtil.killCFG(invoke.node()); + + if (stateAfter.usages().isEmpty()) { + stateAfter.safeDelete(); + } + } + + public static void receiverNullCheck(Invoke invoke) { + MethodCallTargetNode callTarget = invoke.methodCallTarget(); + StructuredGraph graph = (StructuredGraph) invoke.graph(); + NodeInputList parameters = callTarget.arguments(); + ValueNode firstParam = parameters.size() <= 0 ? null : parameters.get(0); + if (!callTarget.isStatic() && firstParam.kind() == Kind.Object && !firstParam.objectStamp().nonNull()) { + graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new IsNullNode(firstParam)), DeoptimizationReason.ClassCastException, DeoptimizationAction.InvalidateReprofile, true, invoke.leafGraphId()))); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InsertStateAfterPlaceholderPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InsertStateAfterPlaceholderPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,67 @@ +/* + * 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.phases.common; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.phases.*; + +public class InsertStateAfterPlaceholderPhase extends Phase { + + private static class PlaceholderNode extends AbstractStateSplit implements StateSplit, Node.IterableNodeType, LIRLowerable, Canonicalizable { + public PlaceholderNode() { + super(StampFactory.forVoid()); + } + + @Override + public void generate(LIRGeneratorTool gen) { + // nothing to do + } + + @Override + public boolean hasSideEffect(CodeCacheProvider runtime) { + return false; + } + + @Override + public ValueNode canonical(CanonicalizerTool tool) { + if (stateAfter() == null) { + return null; + } + return this; + } + } + + @Override + protected void run(StructuredGraph graph) { + for (ReturnNode ret : graph.getNodes(ReturnNode.class)) { + PlaceholderNode p = graph.add(new PlaceholderNode()); + p.setStateAfter(graph.add(new FrameState(FrameState.AFTER_BCI))); + graph.addBeforeFixed(ret, p); + } + } + +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IntrinsificationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IntrinsificationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,77 @@ +/* + * 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.phases.common; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.phases.*; + +public class IntrinsificationPhase extends Phase { + + private final GraalCodeCacheProvider runtime; + + public IntrinsificationPhase(GraalCodeCacheProvider runtime) { + this.runtime = runtime; + } + + @Override + protected void run(StructuredGraph graph) { + for (InvokeNode invoke : graph.getNodes(InvokeNode.class)) { + tryIntrinsify(invoke, runtime); + } + for (InvokeWithExceptionNode invoke : graph.getNodes(InvokeWithExceptionNode.class)) { + tryIntrinsify(invoke, runtime); + } + } + + public static boolean canIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { + return getIntrinsicGraph(invoke, target, runtime) != null; + } + + private static void tryIntrinsify(Invoke invoke, GraalCodeCacheProvider runtime) { + if (invoke.callTarget() instanceof MethodCallTargetNode && invoke.methodCallTarget().targetMethod() != null) { + tryIntrinsify(invoke, invoke.methodCallTarget().targetMethod(), runtime); + } + } + + private static void tryIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { + StructuredGraph intrinsicGraph = getIntrinsicGraph(invoke, target, runtime); + if (intrinsicGraph != null) { + Debug.log(" > Intrinsify %s", target); + InliningUtil.inline(invoke, intrinsicGraph, true); + } + } + + private static StructuredGraph getIntrinsicGraph(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { + StructuredGraph intrinsicGraph = (StructuredGraph) target.compilerStorage().get(Graph.class); + if (intrinsicGraph == null) { + // TODO remove once all intrinsics are available via compilerStorage + intrinsicGraph = runtime.intrinsicGraph(invoke.stateAfter().method(), invoke.bci(), target, invoke.callTarget().arguments()); + } + return intrinsicGraph; + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IterativeCheckCastEliminationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IterativeCheckCastEliminationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,73 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.Graph.InputChangedListener; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; + + +public class IterativeCheckCastEliminationPhase extends Phase { + private final TargetDescription target; + private final MetaAccessProvider runtime; + private final Assumptions assumptions; + + public IterativeCheckCastEliminationPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) { + this.target = target; + this.runtime = runtime; + this.assumptions = assumptions; + } + + @Override + protected void run(StructuredGraph graph) { + Set canonicalizationRoots = new HashSet<>(); + CheckCastEliminationPhase eliminate = new CheckCastEliminationPhase(); + Listener listener = new Listener(canonicalizationRoots); + while (true) { + graph.trackInputChange(listener); + eliminate.apply(graph); + graph.stopTrackingInputChange(); + if (canonicalizationRoots.isEmpty()) { + break; + } + new CanonicalizerPhase(target, runtime, assumptions, canonicalizationRoots, null).apply(graph); + canonicalizationRoots.clear(); + } + } + + private static class Listener implements InputChangedListener { + private final Set canonicalizationRoots; + public Listener(Set canonicalizationRoots) { + this.canonicalizationRoots = canonicalizationRoots; + } + @Override + public void inputChanged(Node node) { + canonicalizationRoots.add(node); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoopSafepointInsertionPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoopSafepointInsertionPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,55 @@ +/* + * 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.phases.common; + +import com.oracle.graal.graph.iterators.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.util.*; + +/** + * Adds safepoints to loops. + */ +public class LoopSafepointInsertionPhase extends Phase { + + @Override + protected void run(StructuredGraph graph) { + nextLoop: + for (LoopEndNode loopEnd : graph.getNodes(LoopEndNode.class)) { + if (!loopEnd.canSafepoint()) { + continue; + } + if (GraalOptions.OptSafepointElimination) { + // We 'eliminate' safepoints by simply never placing them into loops that have at least one call + NodeIterable it = NodeIterators.dominators(loopEnd).until(loopEnd.loopBegin()); + for (FixedNode n : it) { + if (n instanceof Invoke) { + continue nextLoop; + } + } + } + SafepointNode safepoint = graph.add(new SafepointNode()); + graph.addBeforeFixed(loopEnd, safepoint); + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,203 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.iterators.*; +import com.oracle.graal.lir.cfg.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.schedule.*; + +/** + * Processes all {@link Lowerable} nodes to do their lowering. + */ +public class LoweringPhase extends Phase { + + final class LoweringToolImpl implements LoweringTool { + + final FixedNode guardAnchor; + final NodeBitMap activeGuards; + FixedWithNextNode lastFixedNode; + + public LoweringToolImpl(FixedNode guardAnchor, NodeBitMap activeGuards) { + this.guardAnchor = guardAnchor; + this.activeGuards = activeGuards; + } + + @Override + public GraalCodeCacheProvider getRuntime() { + return runtime; + } + + @Override + public ValueNode createNullCheckGuard(ValueNode object, long leafGraphId) { + return createGuard(object.graph().unique(new IsNullNode(object)), DeoptimizationReason.NullCheckException, DeoptimizationAction.InvalidateReprofile, true, leafGraphId); + } + + @Override + public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, long leafGraphId) { + return createGuard(condition, deoptReason, action, false, leafGraphId); + } + + @Override + public Assumptions assumptions() { + return assumptions; + } + + @Override + public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, boolean negated, long leafGraphId) { + if (GraalOptions.OptEliminateGuards) { + for (Node usage : condition.usages()) { + if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage)) { + return (ValueNode) usage; + } + } + } + GuardNode newGuard = guardAnchor.graph().unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, leafGraphId)); + if (GraalOptions.OptEliminateGuards) { + activeGuards.grow(); + activeGuards.mark(newGuard); + } + return newGuard; + } + + public FixedWithNextNode lastFixedNode() { + return lastFixedNode; + } + } + + private final GraalCodeCacheProvider runtime; + private final Assumptions assumptions; + + public LoweringPhase(GraalCodeCacheProvider runtime, Assumptions assumptions) { + this.runtime = runtime; + this.assumptions = assumptions; + } + + private static boolean containsLowerable(NodeIterable nodes) { + for (Node n : nodes) { + if (n instanceof Lowerable) { + return true; + } + } + return false; + } + + @Override + protected void run(final StructuredGraph graph) { + int i = 0; + NodeBitMap processed = graph.createNodeBitMap(); + while (true) { + int mark = graph.getMark(); + final SchedulePhase schedule = new SchedulePhase(); + schedule.apply(graph, false); + + processBlock(schedule.getCFG().getStartBlock(), graph.createNodeBitMap(), null, schedule, processed); + Debug.dump(graph, "Lowering iteration %d", i++); + new CanonicalizerPhase(null, runtime, assumptions, mark, null).apply(graph); + + if (!containsLowerable(graph.getNewNodes(mark))) { + // No new lowerable nodes - done! + break; + } + assert graph.verify(); + processed.grow(); + } + } + + private void processBlock(Block block, NodeBitMap activeGuards, FixedNode parentAnchor, SchedulePhase schedule, NodeBitMap processed) { + + FixedNode anchor = parentAnchor; + if (anchor == null) { + anchor = block.getBeginNode(); + } + process(block, activeGuards, anchor, schedule, processed); + + // Process always reached block first. + Block alwaysReachedBlock = block.getPostdominator(); + if (alwaysReachedBlock != null && alwaysReachedBlock.getDominator() == block) { + processBlock(alwaysReachedBlock, activeGuards, anchor, schedule, processed); + } + + // Now go for the other dominators. + for (Block dominated : block.getDominated()) { + if (dominated != alwaysReachedBlock) { + assert dominated.getDominator() == block; + processBlock(dominated, activeGuards, null, schedule, processed); + } + } + + if (parentAnchor == null && GraalOptions.OptEliminateGuards) { + for (GuardNode guard : anchor.usages().filter(GuardNode.class)) { + activeGuards.clear(guard); + } + } + } + + private void process(final Block b, final NodeBitMap activeGuards, final FixedNode anchor, SchedulePhase schedule, NodeBitMap processed) { + + final LoweringToolImpl loweringTool = new LoweringToolImpl(anchor, activeGuards); + + // Lower the instructions of this block. + List nodes = schedule.nodesFor(b); + + for (Node node : nodes) { + FixedNode lastFixedNext = null; + if (node instanceof FixedWithNextNode) { + FixedWithNextNode fixed = (FixedWithNextNode) node; + lastFixedNext = fixed.next(); + loweringTool.lastFixedNode = fixed; + } + + if (node.isAlive() && !processed.isMarked(node)) { + processed.mark(node); + if (node instanceof Lowerable) { + ((Lowerable) node).lower(loweringTool); + } + } + + if (loweringTool.lastFixedNode == node && !node.isAlive()) { + if (lastFixedNext == null) { + loweringTool.lastFixedNode = null; + } else { + Node prev = lastFixedNext.predecessor(); + if (prev != node && prev instanceof FixedWithNextNode) { + loweringTool.lastFixedNode = (FixedWithNextNode) prev; + } else if (lastFixedNext instanceof FixedWithNextNode) { + loweringTool.lastFixedNode = (FixedWithNextNode) lastFixedNext; + } else { + loweringTool.lastFixedNode = null; + } + } + } + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/PhiStampPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/PhiStampPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,62 @@ +/* + * 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.phases.common; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; + +public class PhiStampPhase extends Phase { + @Override + protected void run(StructuredGraph graph) { + // Infer phis stopping at loop phis. + for (PhiNode phi : graph.getNodes(PhiNode.class)) { + inferPhi(phi); + } + + // Start iterative inference for loop phis. + if (graph.hasLoops()) { + for (PhiNode phi : graph.getNodes(PhiNode.class)) { + if (phi.isLoopPhi()) { + iterativeInferPhi(phi); + } + } + } + } + + private void iterativeInferPhi(PhiNode phi) { + if (phi.inferPhiStamp()) { + for (PhiNode phiUsage : phi.usages().filter(PhiNode.class)) { + iterativeInferPhi(phiUsage); + } + } + } + + private void inferPhi(PhiNode phi) { + for (PhiNode phiInput : phi.values().filter(PhiNode.class)) { + if (!phiInput.isLoopPhi()) { + inferPhi(phiInput); + } + } + phi.inferPhiStamp(); + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,105 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.PhiNode.PhiType; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.phases.*; + +public class ReadEliminationPhase extends Phase { + private Queue newPhis; + + @Override + protected void run(StructuredGraph graph) { + newPhis = new LinkedList<>(); + for (FloatingReadNode n : graph.getNodes(FloatingReadNode.class)) { + if (isReadEliminable(n)) { + NodeMap nodeMap = n.graph().createNodeMap(); + ValueNode value = getValue(n, n.lastLocationAccess(), nodeMap); + Debug.log("Eliminated memory read %1.1s and replaced with node %s", n, value); + graph.replaceFloating(n, value); + } + } + } + + private boolean isReadEliminable(FloatingReadNode n) { + return isWrites(n, n.lastLocationAccess(), n.graph().createNodeBitMap()); + } + + private boolean isWrites(FloatingReadNode n, Node lastLocationAccess, NodeBitMap visited) { + if (lastLocationAccess == null) { + return false; + } + if (visited.isMarked(lastLocationAccess)) { + return true; // dataflow loops must come from Phis assume them ok until proven wrong + } + if (lastLocationAccess instanceof ValueProxyNode) { + return isWrites(n, ((ValueProxyNode) lastLocationAccess).value(), visited); + } + if (lastLocationAccess instanceof WriteNode) { + WriteNode other = (WriteNode) lastLocationAccess; + return other.object() == n.object() && other.location() == n.location(); + } + if (lastLocationAccess instanceof PhiNode) { + visited.mark(lastLocationAccess); + for (ValueNode value : ((PhiNode) lastLocationAccess).values()) { + if (!isWrites(n, value, visited)) { + return false; + } + } + return true; + } + return false; + } + + private ValueNode getValue(FloatingReadNode n, Node lastLocationAccess, NodeMap nodeMap) { + ValueNode exisiting = nodeMap.get(lastLocationAccess); + if (exisiting != null) { + return exisiting; + } + if (lastLocationAccess instanceof ValueProxyNode) { + ValueProxyNode proxy = (ValueProxyNode) lastLocationAccess; + ValueNode value = getValue(n, proxy.value(), nodeMap); + return lastLocationAccess.graph().add(new ValueProxyNode(value, proxy.proxyPoint(), PhiType.Value)); + } + if (lastLocationAccess instanceof WriteNode) { + return ((WriteNode) lastLocationAccess).value(); + } + if (lastLocationAccess instanceof PhiNode) { + PhiNode phi = (PhiNode) lastLocationAccess; + PhiNode newPhi = phi.graph().add(new PhiNode(n.kind(), phi.merge())); + nodeMap.set(lastLocationAccess, newPhi); + for (ValueNode value : phi.values()) { + newPhi.addInput(getValue(n, value, nodeMap)); + } + newPhis.add(newPhi); + return newPhi; + } + throw GraalInternalError.shouldNotReachHere(); + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/RemoveValueProxyPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/RemoveValueProxyPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,45 @@ +/* + * 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.phases.common; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; + +public class RemoveValueProxyPhase extends Phase { + + @Override + protected void run(StructuredGraph graph) { + for (ValueProxyNode vpn : graph.getNodes(ValueProxyNode.class)) { + graph.replaceFloating(vpn, vpn.value()); + } + for (LoopExitNode exit : graph.getNodes(LoopExitNode.class)) { + FrameState stateAfter = exit.stateAfter(); + if (stateAfter != null) { + exit.setStateAfter(null); + if (stateAfter.usages().count() == 0) { + stateAfter.safeDelete(); + } + } + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,551 @@ +/* + * 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.phases.common; + +import java.util.*; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.Graph.DuplicationReplacement; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.VirtualState.NodeClosure; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + +/** + * This class is a phase that looks for opportunities for tail duplication. The static method + * {@link #tailDuplicate(MergeNode, TailDuplicationDecision, List)} can also be used to drive tail duplication from + * other places, e.g., inlining. + */ +public class TailDuplicationPhase extends Phase { + + /* + * Various metrics on the circumstances in which tail duplication was/wasn't performed. + */ + private static final DebugMetric metricDuplicationMonitors = Debug.metric("DuplicationMonitors"); + private static final DebugMetric metricDuplicationEnd = Debug.metric("DuplicationEnd"); + private static final DebugMetric metricDuplicationEndPerformed = Debug.metric("DuplicationEndPerformed"); + private static final DebugMetric metricDuplicationOther = Debug.metric("DuplicationOther"); + private static final DebugMetric metricDuplicationOtherPerformed = Debug.metric("DuplicationOtherPerformed"); + + /** + * This interface is used by tail duplication to let clients decide if tail duplication should be performed. + */ + public interface TailDuplicationDecision { + + /** + * Queries if tail duplication should be performed at the given merge. If this method returns true then the tail + * duplication will be performed, because all other checks have happened before. + * + * @param merge The merge at which tail duplication can be performed. + * @param fixedNodeCount The size of the set of fixed nodes that forms the base for the duplicated set of nodes. + * @return true if the tail duplication should be performed, false otherwise. + */ + boolean doTransform(MergeNode merge, int fixedNodeCount); + } + + /** + * A tail duplication decision closure that employs the default algorithm: Check if there are any phis on the merge + * whose stamps improve and that have usages within the duplicated set of fixed nodes. + */ + public static final TailDuplicationDecision DEFAULT_DECISION = new TailDuplicationDecision() { + + public boolean doTransform(MergeNode merge, int fixedNodeCount) { + if (fixedNodeCount < GraalOptions.TailDuplicationTrivialSize) { + return true; + } + HashSet improvements = new HashSet<>(); + for (PhiNode phi : merge.phis()) { + Stamp phiStamp = phi.stamp(); + for (ValueNode input : phi.values()) { + if (!input.stamp().equals(phiStamp)) { + improvements.add(phi); + break; + } + } + } + if (improvements.isEmpty()) { + return false; + } + FixedNode current = merge; + int opportunities = 0; + while (current instanceof FixedWithNextNode) { + current = ((FixedWithNextNode) current).next(); + for (PhiNode phi : improvements) { + for (Node input : current.inputs()) { + if (input == phi) { + opportunities++; + } + if (input.inputs().contains(phi)) { + opportunities++; + } + } + } + } + return opportunities > 0; + } + }; + + /** + * A tail duplication decision closure that always returns true. + */ + public static final TailDuplicationDecision TRUE_DECISION = new TailDuplicationDecision() { + + @Override + public boolean doTransform(MergeNode merge, int fixedNodeCount) { + return true; + } + }; + + @Override + protected void run(StructuredGraph graph) { + // A snapshot is taken here, so that new MergeNode instances aren't considered for tail duplication. + for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) { + if (!(merge instanceof LoopBeginNode) && merge.probability() >= GraalOptions.TailDuplicationProbability) { + tailDuplicate(merge, DEFAULT_DECISION, null); + } + } + } + + /** + * This method attempts to duplicate the tail of the given merge. The merge must not be a {@link LoopBeginNode}. If + * the merge is eligible for duplication (at least one fixed node in its tail, no {@link MonitorEnterNode}/ + * {@link MonitorExitNode}, non-null {@link MergeNode#stateAfter()}) then the decision callback is used to determine + * whether the tail duplication should actually be performed. If replacements is non-null, then this list of + * {@link PiNode}s is used to replace one value per merge end. + * + * @param merge The merge whose tail should be duplicated. + * @param decision A callback that can make the final decision if tail duplication should occur or not. + * @param replacements A list of {@link PiNode}s, or null. If this list is non-null then its size needs to match the + * merge's end count. Each entry can either be null or a {@link PiNode}, and is used to replace + * {@link PiNode#object()} with the {@link PiNode} in the duplicated branch that corresponds to the + * entry. + */ + public static void tailDuplicate(MergeNode merge, TailDuplicationDecision decision, List replacements) { + assert !(merge instanceof LoopBeginNode); + assert replacements == null || replacements.size() == merge.forwardEndCount(); + FixedNode fixed = merge; + int fixedCount = 0; + boolean containsMonitor = false; + while (fixed instanceof FixedWithNextNode) { + if (fixed instanceof MonitorEnterNode || fixed instanceof MonitorExitNode) { + containsMonitor = true; + } + fixed = ((FixedWithNextNode) fixed).next(); + fixedCount++; + } + if (containsMonitor) { + // cannot currently be handled + // TODO (ls) re-evaluate this limitation after changes to the lock representation and the LIR generator + metricDuplicationMonitors.increment(); + } else if (fixedCount > 1) { + if (fixed instanceof EndNode && !(((EndNode) fixed).merge() instanceof LoopBeginNode)) { + metricDuplicationEnd.increment(); + if (decision.doTransform(merge, fixedCount)) { + metricDuplicationEndPerformed.increment(); + new DuplicationOperation(merge, replacements).duplicate(); + } + } else if (merge.stateAfter() != null) { + metricDuplicationOther.increment(); + if (decision.doTransform(merge, fixedCount)) { + metricDuplicationOtherPerformed.increment(); + new DuplicationOperation(merge, replacements).duplicate(); + } + } + } + } + + /** + * This class encapsulates one tail duplication operation on a specific {@link MergeNode}. + */ + private static class DuplicationOperation { + + private final MergeNode merge; + private final StructuredGraph graph; + + private final HashMap bottomPhis = new HashMap<>(); + private final List replacements; + + /** + * Initializes the tail duplication operation without actually performing any work. + * + * @param merge The merge whose tail should be duplicated. + * @param replacements A list of replacement {@link PiNode}s, or null. If this is non-null, then the size of the + * list needs to match the number of end nodes at the merge. + */ + public DuplicationOperation(MergeNode merge, List replacements) { + this.merge = merge; + this.replacements = replacements; + this.graph = (StructuredGraph) merge.graph(); + } + + /** + * Performs the actual tail duplication: + *
    + *
  • Creates a new {@link ValueAnchorNode} at the beginning of the duplicated area, an transfers all + * dependencies from the merge to this anchor.
  • + *
  • Determines the set of fixed nodes to be duplicated.
  • + *
  • Creates the new merge at the bottom of the duplicated area.
  • + *
  • Determines the complete set of duplicated nodes.
  • + *
  • Performs the actual duplication.
  • + *
+ */ + private void duplicate() { + Debug.log("tail duplication at merge %s in %s (prob %f)", merge, graph.method(), merge.probability()); + + ValueAnchorNode anchor = addValueAnchor(); + + // determine the fixed nodes that should be duplicated (currently: all nodes up until the first control + // split, end node, deopt or return. + ArrayList fixedNodes = new ArrayList<>(); + FixedNode fixed = merge.next(); + FrameState stateAfter = merge.stateAfter(); + while (fixed instanceof FixedWithNextNode) { + fixedNodes.add(fixed); + if (fixed instanceof StateSplit && ((StateSplit) fixed).stateAfter() != null) { + stateAfter = ((StateSplit) fixed).stateAfter(); + } + fixed = ((FixedWithNextNode) fixed).next(); + } + + EndNode endAfter = createNewMerge(fixed, stateAfter); + MergeNode mergeAfter = endAfter.merge(); + fixedNodes.add(endAfter); + final HashSet duplicatedNodes = buildDuplicatedNodeSet(fixedNodes, stateAfter); + mergeAfter.clearEnds(); + expandDuplicated(duplicatedNodes, mergeAfter); + retargetDependencies(duplicatedNodes, anchor); + + List endSnapshot = merge.forwardEnds().snapshot(); + List phiSnapshot = merge.phis().snapshot(); + + int endIndex = 0; + for (final EndNode forwardEnd : merge.forwardEnds()) { + Map duplicates; + if (replacements == null || replacements.get(endIndex) == null) { + duplicates = graph.addDuplicates(duplicatedNodes, (DuplicationReplacement) null); + } else { + HashMap replace = new HashMap<>(); + replace.put(replacements.get(endIndex).object(), replacements.get(endIndex)); + duplicates = graph.addDuplicates(duplicatedNodes, replace); + } + for (Map.Entry phi : bottomPhis.entrySet()) { + phi.getValue().initializeValueAt(merge.forwardEndIndex(forwardEnd), (ValueNode) duplicates.get(phi.getKey())); + } + mergeAfter.addForwardEnd((EndNode) duplicates.get(endAfter)); + + // re-wire the duplicated ValueAnchorNode to the predecessor of the corresponding EndNode + FixedNode anchorDuplicate = (FixedNode) duplicates.get(anchor); + ((FixedWithNextNode) forwardEnd.predecessor()).setNext(anchorDuplicate); + // move dependencies on the ValueAnchorNode to the previous BeginNode + BeginNode prevBegin = BeginNode.prevBegin(anchorDuplicate); + anchorDuplicate.replaceAtUsages(prevBegin); + + // re-wire the phi duplicates to the correct input + for (PhiNode phi : phiSnapshot) { + PhiNode phiDuplicate = (PhiNode) duplicates.get(phi); + for (Node usage : phiDuplicate.usages()) { + if (usage instanceof ValueNode) { + ((ValueNode) usage).dependencies().add(prevBegin); + } + } + phiDuplicate.replaceAtUsages(phi.valueAt(forwardEnd)); + phiDuplicate.safeDelete(); + } + endIndex++; + } + GraphUtil.killCFG(merge); + for (EndNode forwardEnd : endSnapshot) { + forwardEnd.safeDelete(); + } + for (PhiNode phi : phiSnapshot) { + // these phis should go away, but they still need to be anchored to a merge to be valid... + if (phi.isAlive()) { + phi.setMerge(mergeAfter); + } + } + Debug.dump(graph, "After tail duplication at %s", merge); + } + + /** + * Inserts a new ValueAnchorNode after the merge and transfers all dependency-usages (not phis) to this + * ValueAnchorNode. + * + * @return The new {@link ValueAnchorNode} that was created. + */ + private ValueAnchorNode addValueAnchor() { + ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); + graph.addAfterFixed(merge, anchor); + for (Node usage : merge.usages().snapshot()) { + if (usage instanceof PhiNode && ((PhiNode) usage).merge() == merge) { + // nothing to do + } else { + usage.replaceFirstInput(merge, anchor); + } + } + return anchor; + } + + /** + * Given a set of fixed nodes, this method determines the set of fixed and floating nodes that needs to be + * duplicated, i.e., all nodes that due to data flow and other dependencies needs to be duplicated. + * + * @param fixedNodes The set of fixed nodes that should be duplicated. + * @param stateAfter The frame state of the merge that follows the set of fixed nodes. All {@link ValueNode}s + * reachable from this state are considered to be reachable from within the duplicated set of nodes. + * @return The set of nodes that need to be duplicated. + */ + private HashSet buildDuplicatedNodeSet(final ArrayList fixedNodes, FrameState stateAfter) { + final NodeBitMap aboveBound = graph.createNodeBitMap(); + final NodeBitMap belowBound = graph.createNodeBitMap(); + + final Deque worklist = new ArrayDeque<>(); + + // Build the set of nodes that have (transitive) usages within the duplicatedNodes. + // This is achieved by iterating all nodes that are reachable via inputs from the the fixed nodes. + aboveBound.markAll(fixedNodes); + worklist.addAll(fixedNodes); + + // the phis at the original merge should always be duplicated + for (PhiNode phi : merge.phis()) { + aboveBound.mark(phi); + worklist.add(phi); + } + + NodeClosure aboveClosure = new NodeClosure() { + + @Override + public void apply(Node usage, Node node) { + if (node instanceof PhiNode && !fixedNodes.contains(((PhiNode) node).merge())) { + // stop iterating: phis belonging to outside merges are known to be outside. + } else if (node instanceof FixedNode) { + // stop iterating: fixed nodes within the given set are traversal roots anyway, and all other + // fixed nodes are known to be outside. + } else if (!aboveBound.isMarked(node)) { + worklist.add(node); + aboveBound.mark(node); + } + } + }; + + if (stateAfter != null) { + stateAfter.applyToNonVirtual(aboveClosure); + } + while (!worklist.isEmpty()) { + Node current = worklist.remove(); + for (Node input : current.inputs()) { + aboveClosure.apply(current, input); + } + } + + // Build the set of nodes that have (transitive) inputs within the duplicatedNodes. + // This is achieved by iterating all nodes that are reachable via usages from the fixed nodes. + belowBound.markAll(fixedNodes); + worklist.addAll(fixedNodes); + + // the phis at the original merge should always be duplicated + for (PhiNode phi : merge.phis()) { + belowBound.mark(phi); + worklist.add(phi); + } + + while (!worklist.isEmpty()) { + Node current = worklist.remove(); + for (Node usage : current.usages()) { + if (usage instanceof PhiNode && !fixedNodes.contains(((PhiNode) usage).merge())) { + // stop iterating: phis belonging to outside merges are known to be outside. + } else if (usage instanceof FixedNode) { + // stop iterating: fixed nodes within the given set are traversal roots anyway, and all other + // fixed nodes are known to be outside. + } else if (!belowBound.isMarked(usage)) { + worklist.add(usage); + belowBound.mark(usage); + } + } + } + + // build the intersection + belowBound.intersect(aboveBound); + HashSet result = new HashSet<>(); + for (Node node : belowBound) { + result.add(node); + } + return result; + } + + /** + * Creates a new merge and end node construct at the end of the duplicated area. While it is useless in itself + * (merge with only one end) it simplifies the later duplication step. + * + * @param successor The successor of the duplicated set of nodes, i.e., the first node that should not be + * duplicated. + * @param stateAfterMerge The frame state that should be used for the merge. + * @return The newly created end node. + */ + private EndNode createNewMerge(FixedNode successor, FrameState stateAfterMerge) { + MergeNode newBottomMerge = graph.add(new MergeNode()); + newBottomMerge.setProbability(successor.probability()); + EndNode newBottomEnd = graph.add(new EndNode()); + newBottomMerge.addForwardEnd(newBottomEnd); + newBottomMerge.setStateAfter(stateAfterMerge); + ((FixedWithNextNode) successor.predecessor()).setNext(newBottomEnd); + newBottomMerge.setNext(successor); + return newBottomEnd; + } + + /** + * Expands the set of nodes to be duplicated by looking at a number of conditions: + *
    + *
  • {@link ValueNode}s that have usages on the outside need to be replaced with phis for the outside usages.
  • + *
  • Non-{@link ValueNode} nodes that have outside usages (frame states, virtual object states, ...) need to + * be cloned immediately for the outside usages.
  • + *
  • Nodes that have a {@link StampFactory#extension()} or {@link StampFactory#condition()} stamp need to be + * cloned immediately for the outside usages.
  • + *
  • Dependencies into the duplicated nodes will be replaced with dependencies on the merge.
  • + *
  • Outside non-{@link ValueNode}s with usages within the duplicated set of nodes need to also be duplicated. + *
  • + *
  • Outside {@link ValueNode}s with {@link StampFactory#extension()} or {@link StampFactory#condition()} + * stamps that have usages within the duplicated set of nodes need to also be duplicated.
  • + *
+ * + * @param duplicatedNodes The set of duplicated nodes that will be modified (expanded). + * @param newBottomMerge The merge that follows the duplicated set of nodes. It will be used for newly created + * phis and to as a target for dependencies that pointed into the duplicated set of nodes. + */ + private void expandDuplicated(HashSet duplicatedNodes, MergeNode newBottomMerge) { + Deque worklist = new ArrayDeque<>(duplicatedNodes); + + while (!worklist.isEmpty()) { + Node duplicated = worklist.remove(); + if (hasUsagesOutside(duplicated, duplicatedNodes)) { + boolean cloneForOutsideUsages = false; + if (duplicated instanceof ValueNode) { + ValueNode node = (ValueNode) duplicated; + if (node.stamp() == StampFactory.dependency()) { + // re-route dependencies to the merge + replaceUsagesOutside(node, newBottomMerge, duplicatedNodes); + // TODO(ls) maybe introduce phis for dependencies + } else if (node.stamp() == StampFactory.extension() || node.stamp() == StampFactory.condition()) { + cloneForOutsideUsages = true; + } else { + // introduce a new phi + PhiNode newPhi = bottomPhis.get(node); + if (newPhi == null) { + newPhi = graph.add(new PhiNode(node.kind(), newBottomMerge)); + bottomPhis.put(node, newPhi); + newPhi.addInput(node); + } + replaceUsagesOutside(node, newPhi, duplicatedNodes); + } + } else { + cloneForOutsideUsages = true; + } + if (cloneForOutsideUsages) { + // clone the offending node to the outside + Node newOutsideClone = duplicated.copyWithInputs(); + replaceUsagesOutside(duplicated, newOutsideClone, duplicatedNodes); + // this might cause other nodes to have outside usages, we need to look at those as well + for (Node input : newOutsideClone.inputs()) { + if (duplicatedNodes.contains(input)) { + worklist.add(input); + } + } + } + } + // check if this node has an input that lies outside and cannot be shared + for (Node input : duplicated.inputs()) { + if (!duplicatedNodes.contains(input)) { + boolean duplicateInput = false; + if (input instanceof VirtualState) { + duplicateInput = true; + } else if (input instanceof ValueNode) { + Stamp inputStamp = ((ValueNode) input).stamp(); + if (inputStamp == StampFactory.extension() || inputStamp == StampFactory.condition()) { + duplicateInput = true; + } + } + if (duplicateInput) { + duplicatedNodes.add(input); + worklist.add(input); + } + } + } + } + } + + /** + * Moves all depdendencies that point outside the duplicated area to the supplied value anchor node. + * + * @param duplicatedNodes The set of duplicated nodes. + * @param anchor The node that will be the new target for all dependencies that point outside the duplicated set of nodes. + */ + private static void retargetDependencies(HashSet duplicatedNodes, ValueAnchorNode anchor) { + for (Node node : duplicatedNodes) { + if (node instanceof ValueNode) { + NodeInputList dependencies = ((ValueNode) node).dependencies(); + for (int i = 0; i < dependencies.size(); i++) { + Node dependency = dependencies.get(i); + if (dependency != null && !duplicatedNodes.contains(dependency)) { + Debug.log("retargeting dependency %s to %s on %s", dependency, anchor, node); + dependencies.set(i, anchor); + } + } + } + } + } + + /** + * Checks if the given node has usages that are not within the given set of nodes. + * + * @param node The node whose usages are checked. + * @param nodeSet The set of nodes that are considered to be "within". + * @return true if the given node has usages on the outside, false otherwise. + */ + private static boolean hasUsagesOutside(Node node, HashSet nodeSet) { + for (Node usage : node.usages()) { + if (!nodeSet.contains(usage)) { + return true; + } + } + return false; + } + + /** + * Replaces the given node with the given replacement at all usages that are not within the given set of nodes. + * + * @param node The node to be replaced at outside usages. + * @param replacement The node that replaced the given node at outside usages. + * @param nodeSet The set of nodes that are considered to be "within". + */ + private static void replaceUsagesOutside(Node node, Node replacement, HashSet nodeSet) { + for (Node usage : node.usages().snapshot()) { + if (!nodeSet.contains(usage)) { + usage.replaceFirstInput(node, replacement); + } + } + } + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/Phase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/Phase.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,71 @@ +/* + * 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.phases; + +import com.oracle.graal.debug.*; +import com.oracle.graal.nodes.*; + +public abstract class Phase { + + private String name; + private static final DebugMetric metricPhaseRuns = Debug.metric("Runs"); + protected static final DebugMetric METRIC_PROCESSED_NODES = Debug.metric("ProcessedNodes"); + + protected Phase() { + this.name = this.getClass().getSimpleName(); + if (name.endsWith("Phase")) { + name = name.substring(0, name.length() - "Phase".length()); + } + } + + protected Phase(String name) { + this.name = name; + } + + protected String getDetailedName() { + return getName(); + } + + public final void apply(final StructuredGraph graph) { + apply(graph, true); + } + + public final void apply(final StructuredGraph graph, final boolean dumpGraph) { + Debug.scope(name, this, new Runnable() { + public void run() { + Phase.this.run(graph); + metricPhaseRuns.increment(); + if (dumpGraph) { + Debug.dump(graph, "After phase %s", name); + } + assert graph.verify(); + } + }); + } + + public final String getName() { + return name; + } + + protected abstract void run(StructuredGraph graph); +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/PhasePlan.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/PhasePlan.java Sun Oct 07 14:27:50 2012 +0200 @@ -0,0 +1,82 @@ +/* + * 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.phases; + +import java.util.*; + +import com.oracle.graal.nodes.*; + +/** + * Tells the compiler about additional phases that need to be executed during compilation. + */ +public class PhasePlan { + /** + * The compilation is split into the following sections: + * ======================================================================== + * Period 1: High-level nodes. (Graph building) + * ======================================================================== + * Runtime-specific lowering. + * ======================================================================== + * Period 2: Mid-level nodes. (Memory dependence graph) + * ======================================================================== + * Target-specific lowering, de-SSA. + * ======================================================================== + * Period 3: Low-level nodes. (Register allocation, code generation) + * ======================================================================== + * + * A compiler extension phase can chose to run at the end of periods 1-3. + */ + public static enum PhasePosition { + AFTER_PARSING, + HIGH_LEVEL, + MID_LEVEL, + LOW_LEVEL + } + + @SuppressWarnings("unchecked") + private final ArrayList[] phases = new ArrayList[PhasePosition.values().length]; + private final Set> disabledPhases = new HashSet<>(); + + public void addPhase(PhasePosition pos, Phase phase) { + if (phases[pos.ordinal()] == null) { + phases[pos.ordinal()] = new ArrayList<>(); + } + phases[pos.ordinal()].add(phase); + } + + public void runPhases(PhasePosition pos, StructuredGraph graph) { + if (phases[pos.ordinal()] != null) { + for (Phase p : phases[pos.ordinal()]) { + p.apply(graph); + } + } + } + + public void disablePhase(Class clazz) { + disabledPhases.add(clazz); + } + + public boolean isPhaseDisabled(Class clazz) { + return disabledPhases.contains(clazz); + } +} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/BoxingEliminationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/BoxingEliminationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import static com.oracle.graal.graph.iterators.NodePredicates.*; - -import java.util.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.iterators.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.virtual.*; - -public class BoxingEliminationPhase extends Phase { - - private int virtualIds = Integer.MIN_VALUE; - - @Override - protected void run(StructuredGraph graph) { - if (graph.getNodes(UnboxNode.class).isNotEmpty()) { - - Map phiReplacements = new HashMap<>(); - for (UnboxNode unboxNode : graph.getNodes(UnboxNode.class)) { - tryEliminate(graph, unboxNode, phiReplacements); - } - - new DeadCodeEliminationPhase().apply(graph); - - for (BoxNode boxNode : graph.getNodes(BoxNode.class)) { - tryEliminate(boxNode); - } - } - } - - private void tryEliminate(StructuredGraph graph, UnboxNode unboxNode, Map phiReplacements) { - ValueNode unboxedValue = unboxedValue(unboxNode.source(), unboxNode.destinationKind(), phiReplacements); - if (unboxedValue != null) { - assert unboxedValue.kind() == unboxNode.kind(); - unboxNode.replaceAtUsages(unboxedValue); - graph.removeFixed(unboxNode); - } - } - - private PhiNode getReplacementPhi(PhiNode phiNode, Kind kind, Map phiReplacements) { - if (!phiReplacements.containsKey(phiNode)) { - PhiNode result = null; - ObjectStamp stamp = phiNode.objectStamp(); - if (stamp.nonNull() && stamp.isExactType()) { - ResolvedJavaType type = stamp.type(); - if (type != null && type.toJava() == kind.toBoxedJavaClass()) { - StructuredGraph graph = (StructuredGraph) phiNode.graph(); - result = graph.add(new PhiNode(kind, phiNode.merge())); - phiReplacements.put(phiNode, result); - virtualizeUsages(phiNode, result, type); - int i = 0; - for (ValueNode n : phiNode.values()) { - ValueNode unboxedValue = unboxedValue(n, kind, phiReplacements); - if (unboxedValue != null) { - assert unboxedValue.kind() == kind; - result.addInput(unboxedValue); - } else { - UnboxNode unboxNode = graph.add(new UnboxNode(kind, n)); - FixedNode pred = phiNode.merge().phiPredecessorAt(i); - graph.addBeforeFixed(pred, unboxNode); - result.addInput(unboxNode); - } - ++i; - } - } - } - } - return phiReplacements.get(phiNode); - } - - private ValueNode unboxedValue(ValueNode n, Kind kind, Map phiReplacements) { - if (n instanceof BoxNode) { - BoxNode boxNode = (BoxNode) n; - return boxNode.source(); - } else if (n instanceof PhiNode) { - PhiNode phiNode = (PhiNode) n; - return getReplacementPhi(phiNode, kind, phiReplacements); - } else { - return null; - } - } - - private void tryEliminate(BoxNode boxNode) { - - assert boxNode.objectStamp().isExactType(); - virtualizeUsages(boxNode, boxNode.source(), boxNode.objectStamp().type()); - - if (boxNode.usages().filter(isNotA(VirtualState.class)).isNotEmpty()) { - // Elimination failed, because boxing object escapes. - return; - } - - FrameState stateAfter = boxNode.stateAfter(); - boxNode.setStateAfter(null); - stateAfter.safeDelete(); - - ((StructuredGraph) boxNode.graph()).removeFixed(boxNode); - } - - private void virtualizeUsages(ValueNode boxNode, ValueNode replacement, ResolvedJavaType exactType) { - ValueNode virtualValueNode = null; - VirtualObjectNode virtualObjectNode = null; - for (Node n : boxNode.usages().filter(NodePredicates.isA(VirtualState.class)).snapshot()) { - if (virtualValueNode == null) { - virtualObjectNode = n.graph().unique(new BoxedVirtualObjectNode(virtualIds++, exactType, replacement)); - } - n.replaceFirstInput(boxNode, virtualObjectNode); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CanonicalizerPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CanonicalizerPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; -import java.util.concurrent.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.Graph.InputChangedListener; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; - -public class CanonicalizerPhase extends Phase { - private static final int MAX_ITERATION_PER_NODE = 10; - private static final DebugMetric METRIC_CANONICALIZED_NODES = Debug.metric("CanonicalizedNodes"); - private static final DebugMetric METRIC_CANONICALIZATION_CONSIDERED_NODES = Debug.metric("CanonicalizationConsideredNodes"); - private static final DebugMetric METRIC_INFER_STAMP_CALLED = Debug.metric("InferStampCalled"); - private static final DebugMetric METRIC_STAMP_CHANGED = Debug.metric("StampChanged"); - private static final DebugMetric METRIC_SIMPLIFICATION_CONSIDERED_NODES = Debug.metric("SimplificationConsideredNodes"); - public static final DebugMetric METRIC_GLOBAL_VALUE_NUMBERING_HITS = Debug.metric("GlobalValueNumberingHits"); - - private final int newNodesMark; - private final TargetDescription target; - private final Assumptions assumptions; - private final MetaAccessProvider runtime; - private final IsImmutablePredicate immutabilityPredicate; - private final Iterable initWorkingSet; - - private NodeWorkList workList; - private Tool tool; - private List snapshotTemp; - - public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) { - this(target, runtime, assumptions, null, 0, null); - } - - /** - * @param target - * @param runtime - * @param assumptions - * @param workingSet the initial working set of nodes on which the canonicalizer works, should be an auto-grow node bitmap - * @param immutabilityPredicate - */ - public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, Iterable workingSet, IsImmutablePredicate immutabilityPredicate) { - this(target, runtime, assumptions, workingSet, 0, immutabilityPredicate); - } - - /** - * @param newNodesMark only the {@linkplain Graph#getNewNodes(int) new nodes} specified by - * this mark are processed otherwise all nodes in the graph are processed - */ - public CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, int newNodesMark, IsImmutablePredicate immutabilityPredicate) { - this(target, runtime, assumptions, null, newNodesMark, immutabilityPredicate); - } - - private CanonicalizerPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, Iterable workingSet, int newNodesMark, IsImmutablePredicate immutabilityPredicate) { - this.newNodesMark = newNodesMark; - this.target = target; - this.assumptions = assumptions; - this.runtime = runtime; - this.immutabilityPredicate = immutabilityPredicate; - this.initWorkingSet = workingSet; - this.snapshotTemp = new ArrayList<>(); - } - - @Override - protected void run(StructuredGraph graph) { - if (initWorkingSet == null) { - workList = graph.createNodeWorkList(newNodesMark == 0, MAX_ITERATION_PER_NODE); - if (newNodesMark > 0) { - workList.addAll(graph.getNewNodes(newNodesMark)); - } - } else { - workList = graph.createNodeWorkList(false, MAX_ITERATION_PER_NODE); - workList.addAll(initWorkingSet); - } - tool = new Tool(workList, runtime, target, assumptions, immutabilityPredicate); - processWorkSet(graph); - } - - public interface IsImmutablePredicate { - /** - * Determines if a given constant is an object/array whose current - * fields/elements will never change. - */ - boolean apply(Constant constant); - } - - private void processWorkSet(StructuredGraph graph) { - graph.trackInputChange(new InputChangedListener() { - @Override - public void inputChanged(Node node) { - workList.addAgain(node); - } - }); - - for (Node n : workList) { - processNode(n, graph); - } - - graph.stopTrackingInputChange(); - } - - private void processNode(Node node, StructuredGraph graph) { - if (node.isAlive()) { - METRIC_PROCESSED_NODES.increment(); - - if (tryGlobalValueNumbering(node, graph)) { - return; - } - int mark = graph.getMark(); - if (!tryKillUnused(node)) { - node.inputs().filter(GraphUtil.isFloatingNode()).snapshotTo(snapshotTemp); - if (!tryCanonicalize(node, graph, tool)) { - tryInferStamp(node, graph); - } else { - for (Node in : snapshotTemp) { - if (in.isAlive() && in.usages().isEmpty()) { - GraphUtil.killWithUnusedFloatingInputs(in); - } - } - } - snapshotTemp.clear(); - } - - for (Node newNode : graph.getNewNodes(mark)) { - workList.add(newNode); - } - } - } - - private static boolean tryKillUnused(Node node) { - if (node.isAlive() && GraphUtil.isFloatingNode().apply(node) && node.usages().isEmpty()) { - GraphUtil.killWithUnusedFloatingInputs(node); - return true; - } - return false; - } - - public static boolean tryGlobalValueNumbering(Node node, StructuredGraph graph) { - if (node.getNodeClass().valueNumberable()) { - Node newNode = graph.findDuplicate(node); - if (newNode != null) { - assert !(node instanceof FixedNode || newNode instanceof FixedNode); - node.replaceAtUsages(newNode); - node.safeDelete(); - METRIC_GLOBAL_VALUE_NUMBERING_HITS.increment(); - Debug.log("GVN applied and new node is %1s", newNode); - return true; - } - } - return false; - } - - public static boolean tryCanonicalize(final Node node, final StructuredGraph graph, final SimplifierTool tool) { - if (node instanceof Canonicalizable) { - assert !(node instanceof Simplifiable); - METRIC_CANONICALIZATION_CONSIDERED_NODES.increment(); - return Debug.scope("CanonicalizeNode", node, new Callable(){ - public Boolean call() { - ValueNode canonical = ((Canonicalizable) node).canonical(tool); -// cases: original node: -// |Floating|Fixed-unconnected|Fixed-connected| -// -------------------------------------------- -// null| 1 | X | 3 | -// -------------------------------------------- -// Floating| 2 | X | 4 | -// canonical node: -------------------------------------------- -// Fixed-unconnected| X | X | 5 | -// -------------------------------------------- -// Fixed-connected| 2 | X | 6 | -// -------------------------------------------- -// X: must not happen (checked with assertions) - if (canonical == node) { - Debug.log("Canonicalizer: work on %s", node); - return false; - } else { - Debug.log("Canonicalizer: replacing %s with %s", node, canonical); - METRIC_CANONICALIZED_NODES.increment(); - if (node instanceof FloatingNode) { - if (canonical == null) { - // case 1 - graph.removeFloating((FloatingNode) node); - } else { - // case 2 - assert !(canonical instanceof FixedNode) || (canonical.predecessor() != null || canonical instanceof StartNode) : node + " -> " + canonical + - " : replacement should be floating or fixed and connected"; - graph.replaceFloating((FloatingNode) node, canonical); - } - } else { - assert node instanceof FixedWithNextNode && node.predecessor() != null : node + " -> " + canonical + " : node should be fixed & connected (" + node.predecessor() + ")"; - if (canonical == null) { - // case 3 - graph.removeFixed((FixedWithNextNode) node); - } else if (canonical instanceof FloatingNode) { - // case 4 - graph.replaceFixedWithFloating((FixedWithNextNode) node, (FloatingNode) canonical); - } else { - assert canonical instanceof FixedNode; - if (canonical.predecessor() == null) { - assert !canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " shouldn't have successors"; - // case 5 - graph.replaceFixedWithFixed((FixedWithNextNode) node, (FixedWithNextNode) canonical); - } else { - assert canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " should have successors"; - // case 6 - node.replaceAtUsages(canonical); - graph.removeFixed((FixedWithNextNode) node); - } - } - } - return true; - } - } - }); - } else if (node instanceof Simplifiable) { - Debug.log("Canonicalizer: simplifying %s", node); - METRIC_SIMPLIFICATION_CONSIDERED_NODES.increment(); - Debug.scope("SimplifyNode", node, new Runnable() { - public void run() { - ((Simplifiable) node).simplify(tool); - } - }); - } - return node.isDeleted(); - } - - /** - * Calls {@link ValueNode#inferStamp()} on the node and, if it returns true (which means that the stamp has - * changed), re-queues the node's usages . If the stamp has changed then this method also checks if the stamp - * now describes a constant integer value, in which case the node is replaced with a constant. - */ - private void tryInferStamp(Node node, StructuredGraph graph) { - if (node.isAlive() && node instanceof ValueNode) { - ValueNode valueNode = (ValueNode) node; - METRIC_INFER_STAMP_CALLED.increment(); - if (valueNode.inferStamp()) { - METRIC_STAMP_CHANGED.increment(); - if (valueNode.stamp() instanceof IntegerStamp && valueNode.integerStamp().lowerBound() == valueNode.integerStamp().upperBound()) { - ValueNode replacement = ConstantNode.forIntegerKind(valueNode.kind(), valueNode.integerStamp().lowerBound(), graph); - Debug.log("Canonicalizer: replacing %s with %s (inferStamp)", valueNode, replacement); - valueNode.replaceAtUsages(replacement); - } else { - for (Node usage : valueNode.usages()) { - workList.addAgain(usage); - } - } - } - } - } - - private static final class Tool implements SimplifierTool { - - private final NodeWorkList nodeWorkSet; - private final MetaAccessProvider runtime; - private final TargetDescription target; - private final Assumptions assumptions; - private final IsImmutablePredicate immutabilityPredicate; - - public Tool(NodeWorkList nodeWorkSet, MetaAccessProvider runtime, TargetDescription target, Assumptions assumptions, IsImmutablePredicate immutabilityPredicate) { - this.nodeWorkSet = nodeWorkSet; - this.runtime = runtime; - this.target = target; - this.assumptions = assumptions; - this.immutabilityPredicate = immutabilityPredicate; - } - - @Override - public void deleteBranch(FixedNode branch) { - branch.predecessor().replaceFirstSuccessor(branch, null); - GraphUtil.killCFG(branch); - } - - /** - * @return the current target or {@code null} if no target is available in the current context. - */ - @Override - public TargetDescription target() { - return target; - } - - /** - * @return an object that can be used for recording assumptions or {@code null} if assumptions are not allowed in the current context. - */ - @Override - public Assumptions assumptions() { - return assumptions; - } - - @Override - public MetaAccessProvider runtime() { - return runtime; - } - - @Override - public void addToWorkList(Node node) { - nodeWorkSet.add(node); - } - - @Override - public boolean isImmutable(Constant objectConstant) { - return immutabilityPredicate != null && immutabilityPredicate.apply(objectConstant); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CheckCastEliminationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CheckCastEliminationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.graph.*; - -public class CheckCastEliminationPhase extends Phase { - - private static final DebugMetric metricInstanceOfRegistered = Debug.metric("InstanceOfRegistered"); - private static final DebugMetric metricNullCheckRegistered = Debug.metric("NullCheckRegistered"); - private static final DebugMetric metricCheckCastRemoved = Debug.metric("CheckCastRemoved"); - private static final DebugMetric metricInstanceOfRemoved = Debug.metric("InstanceOfRemoved"); - private static final DebugMetric metricNullCheckRemoved = Debug.metric("NullCheckRemoved"); - private static final DebugMetric metricNullCheckGuardRemoved = Debug.metric("NullCheckGuardRemoved"); - private static final DebugMetric metricGuardsReplaced = Debug.metric("GuardsReplaced"); - - private StructuredGraph graph; - - @Override - protected void run(StructuredGraph inputGraph) { - graph = inputGraph; - new EliminateCheckCasts(graph.start(), new State()).apply(); - } - - public static class State implements MergeableState { - - private IdentityHashMap knownTypes; - private HashSet knownNotNull; - private HashSet knownNull; - private IdentityHashMap trueConditions; - private IdentityHashMap falseConditions; - - public State() { - this.knownTypes = new IdentityHashMap<>(); - this.knownNotNull = new HashSet<>(); - this.knownNull = new HashSet<>(); - this.trueConditions = new IdentityHashMap<>(); - this.falseConditions = new IdentityHashMap<>(); - } - - public State(State other) { - this.knownTypes = new IdentityHashMap<>(other.knownTypes); - this.knownNotNull = new HashSet<>(other.knownNotNull); - this.knownNull = new HashSet<>(other.knownNull); - this.trueConditions = new IdentityHashMap<>(other.trueConditions); - this.falseConditions = new IdentityHashMap<>(other.falseConditions); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - IdentityHashMap newKnownTypes = new IdentityHashMap<>(); - HashSet newKnownNotNull = new HashSet<>(); - HashSet newKnownNull = new HashSet<>(); - IdentityHashMap newTrueConditions = new IdentityHashMap<>(); - IdentityHashMap newFalseConditions = new IdentityHashMap<>(); - - for (Map.Entry entry : knownTypes.entrySet()) { - ValueNode node = entry.getKey(); - ResolvedJavaType type = entry.getValue(); - - for (State other : withStates) { - ResolvedJavaType otherType = other.getNodeType(node); - type = widen(type, otherType); - if (type == null) { - break; - } - } - if (type == null && type != node.objectStamp().type()) { - newKnownTypes.put(node, type); - } - } - for (ValueNode node : knownNotNull) { - boolean notNull = true; - for (State other : withStates) { - if (!other.knownNotNull.contains(node)) { - notNull = false; - break; - } - } - if (notNull) { - newKnownNotNull.add(node); - } - } - for (ValueNode node : knownNull) { - boolean nul = true; - for (State other : withStates) { - if (!other.knownNull.contains(node)) { - nul = false; - break; - } - } - if (nul) { - newKnownNull.add(node); - } - } - for (Map.Entry entry : trueConditions.entrySet()) { - BooleanNode check = entry.getKey(); - ValueNode guard = entry.getValue(); - - for (State other : withStates) { - ValueNode otherGuard = other.trueConditions.get(check); - if (otherGuard == null) { - guard = null; - break; - } - if (otherGuard != guard) { - guard = merge; - } - } - if (guard != null) { - newTrueConditions.put(check, guard); - } - } - for (Map.Entry entry : falseConditions.entrySet()) { - BooleanNode check = entry.getKey(); - ValueNode guard = entry.getValue(); - - for (State other : withStates) { - ValueNode otherGuard = other.falseConditions.get(check); - if (otherGuard == null) { - guard = null; - break; - } - if (otherGuard != guard) { - guard = merge; - } - } - if (guard != null) { - newFalseConditions.put(check, guard); - } - } - - /* - // this piece of code handles phis (merges the types and knownNull/knownNotNull of the values) - if (!(merge instanceof LoopBeginNode)) { - for (PhiNode phi : merge.phis()) { - if (phi.type() == PhiType.Value && phi.kind() == Kind.Object) { - ValueNode firstValue = phi.valueAt(0); - ResolvedJavaType type = getNodeType(firstValue); - boolean notNull = knownNotNull.contains(firstValue); - boolean nul = knownNull.contains(firstValue); - - for (int i = 0; i < withStates.size(); i++) { - State otherState = withStates.get(i); - ValueNode value = phi.valueAt(i + 1); - ResolvedJavaType otherType = otherState.getNodeType(value); - type = widen(type, otherType); - notNull &= otherState.knownNotNull.contains(value); - nul &= otherState.knownNull.contains(value); - } - if (type == null && type != phi.declaredType()) { - newKnownTypes.put(phi, type); - } - if (notNull) { - newKnownNotNull.add(phi); - } - if (nul) { - newKnownNull.add(phi); - } - } - } - } - */ - this.knownTypes = newKnownTypes; - this.knownNotNull = newKnownNotNull; - this.knownNull = newKnownNull; - this.trueConditions = newTrueConditions; - this.falseConditions = newFalseConditions; - return true; - } - - public ResolvedJavaType getNodeType(ValueNode node) { - ResolvedJavaType result = knownTypes.get(node); - return result == null ? node.objectStamp().type() : result; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - } - - @Override - public void afterSplit(FixedNode node) { - } - - @Override - public State clone() { - return new State(this); - } - } - - public static ResolvedJavaType widen(ResolvedJavaType a, ResolvedJavaType b) { - if (a == null || b == null) { - return null; - } else if (a == b) { - return a; - } else { - return a.leastCommonAncestor(b); - } - } - - public static ResolvedJavaType tighten(ResolvedJavaType a, ResolvedJavaType b) { - if (a == null) { - return b; - } else if (b == null) { - return a; - } else if (a == b) { - return a; - } else if (a.isSubtypeOf(b)) { - return a; - } else if (b.isSubtypeOf(a)) { - return b; - } else { - return a; - } - } - - public class EliminateCheckCasts extends PostOrderNodeIterator { - private BeginNode lastBegin = null; - - public EliminateCheckCasts(FixedNode start, State initialState) { - super(start, initialState); - } - - @Override - protected void node(FixedNode node) { - if (node instanceof BeginNode) { - BeginNode begin = (BeginNode) node; - lastBegin = begin; - Node pred = node.predecessor(); - if (pred != null && pred instanceof IfNode) { - IfNode ifNode = (IfNode) pred; - if (!(ifNode.compare() instanceof ConstantNode)) { - boolean isTrue = (node == ifNode.trueSuccessor()); - if (isTrue) { - state.trueConditions.put(ifNode.compare(), begin); - } else { - state.falseConditions.put(ifNode.compare(), begin); - } - } - if (ifNode.compare() instanceof InstanceOfNode) { - InstanceOfNode instanceOf = (InstanceOfNode) ifNode.compare(); - if ((node == ifNode.trueSuccessor())) { - ValueNode object = instanceOf.object(); - state.knownNotNull.add(object); - state.knownTypes.put(object, tighten(instanceOf.targetClass(), state.getNodeType(object))); - metricInstanceOfRegistered.increment(); - } - } else if (ifNode.compare() instanceof IsNullNode) { - IsNullNode nullCheck = (IsNullNode) ifNode.compare(); - boolean isNull = (node == ifNode.trueSuccessor()); - if (isNull) { - state.knownNull.add(nullCheck.object()); - } else { - state.knownNotNull.add(nullCheck.object()); - } - metricNullCheckRegistered.increment(); - } - } - for (GuardNode guard : begin.guards().snapshot()) { - BooleanNode condition = guard.condition(); - ValueNode existingGuards = guard.negated() ? state.falseConditions.get(condition) : state.trueConditions.get(condition); - if (existingGuards != null) { - guard.replaceAtUsages(existingGuards); - GraphUtil.killWithUnusedFloatingInputs(guard); - metricGuardsReplaced.increment(); - } else { - boolean removeCheck = false; - if (condition instanceof IsNullNode) { - IsNullNode isNull = (IsNullNode) condition; - if (guard.negated() && state.knownNotNull.contains(isNull.object())) { - removeCheck = true; - } else if (!guard.negated() && state.knownNull.contains(isNull.object())) { - removeCheck = true; - } - if (removeCheck) { - metricNullCheckGuardRemoved.increment(); - } - } - if (removeCheck) { - guard.replaceAtUsages(begin); - GraphUtil.killWithUnusedFloatingInputs(guard); - } else { - if (guard.negated()) { - state.falseConditions.put(condition, guard); - } else { - state.trueConditions.put(condition, guard); - } - } - } - } - } else if (node instanceof CheckCastNode) { - CheckCastNode checkCast = (CheckCastNode) node; - ResolvedJavaType type = state.getNodeType(checkCast.object()); - if (checkCast.targetClass() != null && type != null && type.isSubtypeOf(checkCast.targetClass())) { - PiNode piNode; - boolean nonNull = state.knownNotNull.contains(checkCast.object()); - piNode = graph.unique(new PiNode(checkCast.object(), lastBegin, nonNull ? StampFactory.declaredNonNull(type) : StampFactory.declared(type))); - checkCast.replaceAtUsages(piNode); - graph.removeFixed(checkCast); - metricCheckCastRemoved.increment(); - } - } else if (node instanceof IfNode) { - IfNode ifNode = (IfNode) node; - BooleanNode replaceWith = null; - BooleanNode compare = ifNode.compare(); - - if (state.trueConditions.containsKey(compare)) { - replaceWith = ConstantNode.forBoolean(true, graph); - } else if (state.falseConditions.containsKey(compare)) { - replaceWith = ConstantNode.forBoolean(false, graph); - } else { - if (compare instanceof InstanceOfNode) { - InstanceOfNode instanceOf = (InstanceOfNode) compare; - ValueNode object = instanceOf.object(); - if (state.knownNull.contains(object)) { - replaceWith = ConstantNode.forBoolean(false, graph); - } else if (state.knownNotNull.contains(object)) { - ResolvedJavaType type = state.getNodeType(object); - if (type != null && type.isSubtypeOf(instanceOf.targetClass())) { - replaceWith = ConstantNode.forBoolean(true, graph); - } - } - if (replaceWith != null) { - metricInstanceOfRemoved.increment(); - } - } else if (compare instanceof IsNullNode) { - IsNullNode isNull = (IsNullNode) compare; - ValueNode object = isNull.object(); - if (state.knownNull.contains(object)) { - replaceWith = ConstantNode.forBoolean(true, graph); - } else if (state.knownNotNull.contains(object)) { - replaceWith = ConstantNode.forBoolean(false, graph); - } - if (replaceWith != null) { - metricNullCheckRemoved.increment(); - } - } - } - if (replaceWith != null) { - ifNode.setCompare(replaceWith); - if (compare.usages().isEmpty()) { - GraphUtil.killWithUnusedFloatingInputs(compare); - } - } - } - } - } - -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ComputeProbabilityPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ComputeProbabilityPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -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.phases.*; -import com.oracle.graal.phases.graph.*; - -public class ComputeProbabilityPhase extends Phase { - private static final double EPSILON = 1d / Integer.MAX_VALUE; - - /* - * The computation of absolute probabilities works in three steps: - * - * - The first step, "PropagateProbability", traverses the graph in post order (merges after their ends, ...) and keeps track of the "probability state". - * Whenever it encounters a ControlSplit it uses the split's probability information to divide the probability upon the successors. - * Whenever it encounters an Invoke it assumes that the exception edge is unlikely and propagates the whole probability to the normal successor. - * Whenever it encounters a Merge it sums up the probability of all predecessors. - * It also maintains a set of active loops (whose LoopBegin has been visited) and builds def/use information for the second step. - * - * - The third step propagates the loop frequencies and multiplies each FixedNode's probability with its loop frequency. - * - * TODO: add exception probability information to Invokes - */ - - @Override - protected void run(StructuredGraph graph) { - new PropagateProbability(graph.start()).apply(); - Debug.dump(graph, "After PropagateProbability"); - computeLoopFactors(); - Debug.dump(graph, "After computeLoopFactors"); - new PropagateLoopFrequency(graph.start()).apply(); - - if (GraalOptions.LoopFrequencyPropagationPolicy < 0) { - ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false); - BitSet visitedBlocks = new BitSet(cfg.getBlocks().length); - for (Loop loop : cfg.getLoops()) { - if (loop.parent == null) { - correctLoopFrequencies(loop, 1, visitedBlocks); - } - } - } - } - - private void correctLoopFrequencies(Loop loop, double parentFrequency, BitSet visitedBlocks) { - LoopBeginNode loopBegin = ((LoopBeginNode) loop.header.getBeginNode()); - double frequency = parentFrequency * loopBegin.loopFrequency(); - for (Loop child : loop.children) { - correctLoopFrequencies(child, frequency, visitedBlocks); - } - - double factor = getCorrectionFactor(loopBegin.probability(), frequency); - for (Block block : loop.blocks) { - int blockId = block.getId(); - if (!visitedBlocks.get(blockId)) { - visitedBlocks.set(blockId); - - FixedNode node = block.getBeginNode(); - while (node != block.getEndNode()) { - node.setProbability(node.probability() * factor); - node = ((FixedWithNextNode) node).next(); - } - node.setProbability(node.probability() * factor); - } - } - } - - private static double getCorrectionFactor(double probability, double frequency) { - switch (GraalOptions.LoopFrequencyPropagationPolicy) { - case -1: - return 1 / frequency; - case -2: - return (1 / frequency) * (Math.log(Math.E + frequency) - 1); - case -3: - double originalProbability = probability / frequency; - assert isRelativeProbability(originalProbability); - return (1 / frequency) * Math.max(1, Math.pow(originalProbability, 1.5) * Math.log10(frequency)); - default: - throw GraalInternalError.shouldNotReachHere(); - } - } - - private void computeLoopFactors() { - for (LoopInfo info : loopInfos) { - double frequency = info.loopFrequency(); - assert frequency != -1; - } - } - - private static boolean isRelativeProbability(double prob) { - // 1.01 to allow for some rounding errors - return prob >= 0 && prob <= 1.01; - } - - public static class LoopInfo { - public final LoopBeginNode loopBegin; - - public final NodeMap> requires; - - private double loopFrequency = -1; - public boolean ended = false; - - public LoopInfo(LoopBeginNode loopBegin) { - this.loopBegin = loopBegin; - this.requires = loopBegin.graph().createNodeMap(); - } - - public double loopFrequency() { - if (loopFrequency == -1 && ended) { - double backEdgeProb = 0.0; - for (LoopEndNode le : loopBegin.loopEnds()) { - double factor = 1; - Set requireds = requires.get(le); - for (LoopInfo required : requireds) { - double t = required.loopFrequency(); - if (t == -1) { - return -1; - } - factor *= t; - } - backEdgeProb += le.probability() * factor; - } - double d = loopBegin.probability() - backEdgeProb; - if (d < EPSILON) { - d = EPSILON; - } - loopFrequency = loopBegin.probability() / d; - loopBegin.setLoopFrequency(loopFrequency); - } - return loopFrequency; - } - } - - public Set loopInfos = new HashSet<>(); - public Map> mergeLoops = new IdentityHashMap<>(); - - private class Probability implements MergeableState { - public double probability; - public HashSet loops; - public LoopInfo loopInfo; - - public Probability(double probability, HashSet loops) { - this.probability = probability; - this.loops = new HashSet<>(4); - if (loops != null) { - this.loops.addAll(loops); - } - } - - @Override - public Probability clone() { - return new Probability(probability, loops); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - if (merge.forwardEndCount() > 1) { - HashSet intersection = new HashSet<>(loops); - for (Probability other : withStates) { - intersection.retainAll(other.loops); - } - for (LoopInfo info : loops) { - if (!intersection.contains(info)) { - double loopFrequency = info.loopFrequency(); - if (loopFrequency == -1) { - return false; - } - probability *= loopFrequency; - } - } - for (Probability other : withStates) { - double prob = other.probability; - for (LoopInfo info : other.loops) { - if (!intersection.contains(info)) { - double loopFrequency = info.loopFrequency(); - if (loopFrequency == -1) { - return false; - } - prob *= loopFrequency; - } - } - probability += prob; - } - loops = intersection; - mergeLoops.put(merge, new HashSet<>(intersection)); - assert isRelativeProbability(probability) : probability; - } - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - loopInfo = new LoopInfo(loopBegin); - loopInfos.add(loopInfo); - loops.add(loopInfo); - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - assert loopInfo != null; - List loopEnds = loopBegin.orderedLoopEnds(); - int i = 0; - for (Probability proba : loopEndStates) { - LoopEndNode loopEnd = loopEnds.get(i++); - Set requires = loopInfo.requires.get(loopEnd); - if (requires == null) { - requires = new HashSet<>(); - loopInfo.requires.set(loopEnd, requires); - } - for (LoopInfo innerLoop : proba.loops) { - if (innerLoop != loopInfo && !this.loops.contains(innerLoop)) { - requires.add(innerLoop); - } - } - } - loopInfo.ended = true; - } - - @Override - public void afterSplit(FixedNode node) { - assert node.predecessor() != null; - Node pred = node.predecessor(); - if (pred instanceof Invoke) { - Invoke x = (Invoke) pred; - if (x.next() != node) { - probability = 0; - } - } else { - assert pred instanceof ControlSplitNode; - ControlSplitNode x = (ControlSplitNode) pred; - double sum = 0; - for (int i = 0; i < x.blockSuccessorCount(); i++) { - if (x.blockSuccessor(i) == node) { - sum += x.probability(i); - } - } - probability *= sum; - } - } - } - - private class PropagateProbability extends PostOrderNodeIterator { - - public PropagateProbability(FixedNode start) { - super(start, new Probability(1d, null)); - } - - @Override - protected void node(FixedNode node) { - node.setProbability(state.probability); - } - } - - private class LoopCount implements MergeableState { - public double count; - - public LoopCount(double count) { - this.count = count; - } - - @Override - public LoopCount clone() { - return new LoopCount(count); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - assert merge.forwardEndCount() == withStates.size() + 1; - if (merge.forwardEndCount() > 1) { - Set loops = mergeLoops.get(merge); - assert loops != null; - double countProd = 1; - for (LoopInfo loop : loops) { - countProd *= loop.loopFrequency(); - } - count = countProd; - } - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - count *= loopBegin.loopFrequency(); - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - // nothing to do... - } - - @Override - public void afterSplit(FixedNode node) { - // nothing to do... - } - } - - private class PropagateLoopFrequency extends PostOrderNodeIterator { - - private final FrequencyPropagationPolicy policy; - - public PropagateLoopFrequency(FixedNode start) { - super(start, new LoopCount(1d)); - this.policy = createFrequencyPropagationPolicy(); - } - - @Override - protected void node(FixedNode node) { - node.setProbability(policy.compute(node.probability(), state.count)); - } - - } - - private static FrequencyPropagationPolicy createFrequencyPropagationPolicy() { - switch (GraalOptions.LoopFrequencyPropagationPolicy) { - case -3: - case -2: - case -1: - case 0: - return new FullFrequencyPropagation(); - case 1: - return new NoFrequencyPropagation(); - default: - throw GraalInternalError.shouldNotReachHere(); - } - } - - private interface FrequencyPropagationPolicy { - - double compute(double probability, double frequency); - } - - private static class FullFrequencyPropagation implements FrequencyPropagationPolicy { - - @Override - public double compute(double probability, double frequency) { - return probability * frequency; - } - } - - private static class NoFrequencyPropagation implements FrequencyPropagationPolicy { - - @Override - public double compute(double probability, double frequency) { - return probability; - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ConvertDeoptimizeToGuardPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ConvertDeoptimizeToGuardPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; - -public class ConvertDeoptimizeToGuardPhase extends Phase { - - private static BeginNode findBeginNode(Node startNode) { - Node n = startNode; - while (true) { - if (n instanceof BeginNode) { - return (BeginNode) n; - } else { - n = n.predecessor(); - } - } - } - - @Override - protected void run(final StructuredGraph graph) { - if (graph.getNodes(DeoptimizeNode.class).isEmpty()) { - return; - } - - for (DeoptimizeNode d : graph.getNodes(DeoptimizeNode.class)) { - visitDeoptBegin(findBeginNode(d), d, graph); - } - - new DeadCodeEliminationPhase().apply(graph); - } - - private void visitDeoptBegin(BeginNode deoptBegin, DeoptimizeNode deopt, StructuredGraph graph) { - if (deoptBegin instanceof MergeNode) { - MergeNode mergeNode = (MergeNode) deoptBegin; - Debug.log("Visiting %s followed by %s", mergeNode, deopt); - List ends = mergeNode.forwardEnds().snapshot(); - for (EndNode end : ends) { - if (!end.isDeleted()) { - BeginNode beginNode = findBeginNode(end); - if (!(beginNode instanceof MergeNode)) { - visitDeoptBegin(beginNode, deopt, graph); - } - } - } - if (mergeNode.isDeleted()) { - if (!deopt.isDeleted()) { - Debug.log("Merge deleted, deopt moved to %s", findBeginNode(deopt)); - visitDeoptBegin(findBeginNode(deopt), deopt, graph); - } - } - } else if (deoptBegin.predecessor() instanceof IfNode) { - IfNode ifNode = (IfNode) deoptBegin.predecessor(); - BeginNode otherBegin = ifNode.trueSuccessor(); - BooleanNode conditionNode = ifNode.compare(); - boolean negated = false; - if (deoptBegin == ifNode.trueSuccessor()) { - negated = true; - otherBegin = ifNode.falseSuccessor(); - } - BeginNode ifBlockBegin = findBeginNode(ifNode); - Debug.log("Converting %s on %-5s branch of %s to guard for remaining branch %s. IfBegin=%s", deopt, deoptBegin == ifNode.trueSuccessor() ? "true" : "false", ifNode, otherBegin, ifBlockBegin); - FixedGuardNode guard = graph.add(new FixedGuardNode(conditionNode, deopt.reason(), deopt.action(), negated, deopt.leafGraphId())); - otherBegin.replaceAtUsages(ifBlockBegin); - FixedNode next = otherBegin.next(); - otherBegin.setNext(null); - guard.setNext(next); - ifNode.replaceAtPredecessor(guard); - GraphUtil.killCFG(ifNode); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ConvertUnreachedToGuardPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ConvertUnreachedToGuardPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; - - -public class ConvertUnreachedToGuardPhase extends Phase { - private OptimisticOptimizations opt; - - public ConvertUnreachedToGuardPhase(OptimisticOptimizations opt) { - this.opt = opt; - } - - @Override - protected void run(StructuredGraph graph) { - if (!opt.removeNeverExecutedCode()) { - return; - } - for (Node node : graph.getNodes()) { - if (node instanceof IfNode) { - IfNode ifNode = (IfNode) node; - BeginNode insertGuard = null; - BeginNode delete = null; - boolean inverted = false; - if (ifNode.probability(IfNode.TRUE_EDGE) == 0) { - insertGuard = ifNode.falseSuccessor(); - delete = ifNode.trueSuccessor(); - inverted = true; - } else if (ifNode.probability(IfNode.FALSE_EDGE) == 0) { - insertGuard = ifNode.trueSuccessor(); - delete = ifNode.falseSuccessor(); - } - if (insertGuard != null) { - GuardNode guard = graph.unique(new GuardNode(ifNode.compare(), BeginNode.prevBegin(ifNode), DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, inverted, ifNode.leafGraphId())); - graph.addBeforeFixed(ifNode, graph.add(new ValueAnchorNode(guard))); - GraphUtil.killCFG(delete); - graph.removeSplit(ifNode, inverted ? IfNode.FALSE_EDGE : IfNode.TRUE_EDGE); - } - } - } - - } - -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CullFrameStatesPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/CullFrameStatesPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.debug.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.graph.*; - -/** - * This phase culls unused FrameStates from the graph. - * It does a post order iteration over the graph, and - */ -public class CullFrameStatesPhase extends Phase { - - private static final DebugMetric metricFrameStatesCulled = Debug.metric("FrameStatesCulled"); - private static final DebugMetric metricMergesTraversed = Debug.metric("MergesTraversed"); - - @Override - protected void run(StructuredGraph graph) { - new CullFrameStates(graph.start(), new State(null)).apply(); - } - - public static class State implements MergeableState { - - private FrameState lastFrameState; - - public State(FrameState lastFrameState) { - this.lastFrameState = lastFrameState; - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - FrameState stateAfter = merge.stateAfter(); - if (merge instanceof LoopBeginNode) { - if (stateAfter != null) { - lastFrameState = stateAfter; - } - return true; - } - metricMergesTraversed.increment(); - if (stateAfter != null) { - for (State other : withStates) { - if (other.lastFrameState != lastFrameState) { - lastFrameState = stateAfter; - return true; - } - } - metricFrameStatesCulled.increment(); - merge.setStateAfter(null); - if (stateAfter.usages().isEmpty()) { - GraphUtil.killWithUnusedFloatingInputs(stateAfter); - } - } - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - } - - @Override - public void afterSplit(FixedNode node) { - } - - @Override - public State clone() { - return new State(lastFrameState); - } - } - - public static class CullFrameStates extends PostOrderNodeIterator { - - public CullFrameStates(FixedNode start, State initialState) { - super(start, initialState); - } - - @Override - protected void node(FixedNode node) { - if (node instanceof StateSplit) { - FrameState stateAfter = ((StateSplit) node).stateAfter(); - if (stateAfter != null) { - state.lastFrameState = stateAfter; - } - } - } - } - -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/DeadCodeEliminationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/DeadCodeEliminationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - - -public class DeadCodeEliminationPhase extends Phase { - - // Metrics - private static final DebugMetric metricNodesRemoved = Debug.metric("NodesRemoved"); - - private NodeFlood flood; - - @Override - protected void run(StructuredGraph graph) { - this.flood = graph.createNodeFlood(); - - flood.add(graph.start()); - iterateSuccessors(); - disconnectCFGNodes(graph); - iterateInputs(graph); - deleteNodes(graph); - - // remove chained Merges - for (MergeNode merge : graph.getNodes(MergeNode.class)) { - if (merge.forwardEndCount() == 1 && !(merge instanceof LoopBeginNode)) { - graph.reduceTrivialMerge(merge); - } - } - } - - private void iterateSuccessors() { - for (Node current : flood) { - if (current instanceof EndNode) { - EndNode end = (EndNode) current; - flood.add(end.merge()); - } else { - for (Node successor : current.successors()) { - flood.add(successor); - } - } - } - } - - private void disconnectCFGNodes(StructuredGraph graph) { - for (EndNode node : graph.getNodes(EndNode.class)) { - if (!flood.isMarked(node)) { - MergeNode merge = node.merge(); - if (merge != null && flood.isMarked(merge)) { - // We are a dead end node leading to a live merge. - merge.removeEnd(node); - } - } - } - for (LoopBeginNode loop : graph.getNodes(LoopBeginNode.class)) { - if (flood.isMarked(loop)) { - boolean reachable = false; - for (LoopEndNode end : loop.loopEnds()) { - if (flood.isMarked(end)) { - reachable = true; - break; - } - } - if (!reachable) { - Debug.log("Removing loop with unreachable end: %s", loop); - for (LoopEndNode end : loop.loopEnds().snapshot()) { - loop.removeEnd(end); - } - graph.reduceDegenerateLoopBegin(loop); - } - } - } - } - - private void deleteNodes(StructuredGraph graph) { - for (Node node : graph.getNodes()) { - if (!flood.isMarked(node)) { - node.clearInputs(); - node.clearSuccessors(); - } - } - for (Node node : graph.getNodes()) { - if (!flood.isMarked(node)) { - metricNodesRemoved.increment(); - node.safeDelete(); - } - } - } - - private void iterateInputs(StructuredGraph graph) { - for (Node node : graph.getNodes()) { - if (node instanceof LocalNode) { - flood.add(node); - } - if (flood.isMarked(node)) { - for (Node input : node.inputs()) { - flood.add(input); - } - } - } - for (Node current : flood) { - for (Node input : current.inputs()) { - flood.add(input); - } - } - } - -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ExpandBoxingNodesPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ExpandBoxingNodesPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.extended.*; - -public class ExpandBoxingNodesPhase extends Phase { - - private final BoxingMethodPool pool; - - public ExpandBoxingNodesPhase(BoxingMethodPool pool) { - this.pool = pool; - } - - @Override - protected void run(StructuredGraph graph) { - for (BoxNode boxNode : graph.getNodes(BoxNode.class)) { - boxNode.expand(pool); - } - - for (UnboxNode unboxNode : graph.getNodes(UnboxNode.class)) { - unboxNode.expand(pool); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/FloatingReadPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/FloatingReadPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,294 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.PhiNode.PhiType; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.phases.graph.*; - -public class FloatingReadPhase extends Phase { - - private IdentityHashMap> loopEndStatesMap; - - private static class LoopState { - public LoopBeginNode loopBegin; - public MemoryMap state; - public IdentityHashMap loopPhiLocations = new IdentityHashMap<>(); - public ValueNode loopEntryAnyLocation; - public LoopState(LoopBeginNode loopBegin, MemoryMap state, ValueNode loopEntryAnyLocation) { - this.loopBegin = loopBegin; - this.state = state; - this.loopEntryAnyLocation = loopEntryAnyLocation; - } - - @Override - public String toString() { - return "State@" + loopBegin; - } - } - - private class MemoryMap implements MergeableState { - private IdentityHashMap lastMemorySnapshot; - private LinkedList loops; - - public MemoryMap(MemoryMap memoryMap) { - lastMemorySnapshot = new IdentityHashMap<>(memoryMap.lastMemorySnapshot); - loops = new LinkedList<>(memoryMap.loops); - } - - public MemoryMap() { - lastMemorySnapshot = new IdentityHashMap<>(); - loops = new LinkedList<>(); - } - - @Override - public String toString() { - return "Map=" + lastMemorySnapshot.toString() + " Loops=" + loops.toString(); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - if (withStates.size() == 0) { - return true; - } - - int minLoops = loops.size(); - for (MemoryMap other : withStates) { - int otherLoops = other.loops.size(); - if (otherLoops < minLoops) { - minLoops = otherLoops; - } - } - while (loops.size() > minLoops) { - loops.pop(); - } - for (MemoryMap other : withStates) { - while (other.loops.size() > minLoops) { - other.loops.pop(); - } - } - - Set keys = new HashSet<>(); - for (Object key : lastMemorySnapshot.keySet()) { - keys.add(key); - } - for (MemoryMap other : withStates) { - assert other.loops.size() == loops.size(); - assert other.loops.size() < 1 || other.loops.peek().loopBegin == loops.peek().loopBegin; - for (Object key : other.lastMemorySnapshot.keySet()) { - keys.add(key); - } - } - @SuppressWarnings("unchecked") - IdentityHashMap newMemorySnapshot = (IdentityHashMap) lastMemorySnapshot.clone(); - - for (Object key : keys) { - ValueNode merged = lastMemorySnapshot.get(key); - if (merged == null) { - merged = lastMemorySnapshot.get(LocationNode.ANY_LOCATION); - } - int mergedStatesCount = 1; - boolean isPhi = false; - for (MemoryMap other : withStates) { - ValueNode otherValue = other.lastMemorySnapshot.get(key); - if (otherValue == null) { - otherValue = other.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); - } - if (isPhi) { - ((PhiNode) merged).addInput(otherValue); - } else if (merged != otherValue) { - PhiNode phi = merge.graph().add(new PhiNode(PhiType.Memory, merge)); - for (int j = 0; j < mergedStatesCount; j++) { - phi.addInput(merged); - } - phi.addInput(otherValue); - merged = phi; - isPhi = true; - newMemorySnapshot.put(key, phi); - } - mergedStatesCount++; - } - } - - lastMemorySnapshot = newMemorySnapshot; - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - LoopState loopState = new LoopState(loopBegin, this, lastMemorySnapshot.get(LocationNode.ANY_LOCATION)); - for (Map.Entry entry : lastMemorySnapshot.entrySet()) { - PhiNode phi = loopBegin.graph().add(new PhiNode(PhiType.Memory, loopBegin)); - phi.addInput(entry.getValue()); - entry.setValue(phi); - loopState.loopPhiLocations.put(phi, entry.getKey()); - } - loops.push(loopState); - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - loopEndStatesMap.put(loopBegin, loopEndStates); - tryFinishLoopPhis(this, loopBegin); - } - - @Override - public void afterSplit(FixedNode node) { - // nothing - } - - @Override - public MemoryMap clone() { - return new MemoryMap(this); - } - } - - @Override - protected void run(StructuredGraph graph) { - loopEndStatesMap = new IdentityHashMap<>(); - new PostOrderNodeIterator(graph.start(), new MemoryMap()) { - @Override - protected void node(FixedNode node) { - processNode(node, state); - } - }.apply(); - } - - private void processNode(FixedNode node, MemoryMap state) { - if (node instanceof ReadNode) { - processRead((ReadNode) node, state); - } else if (node instanceof WriteNode) { - processWrite((WriteNode) node, state); - } else if (node instanceof MemoryCheckpoint) { - processCheckpoint((MemoryCheckpoint) node, state); - } else if (node instanceof LoopExitNode) { - processLoopExit((LoopExitNode) node, state); - } - } - - private static void processCheckpoint(MemoryCheckpoint checkpoint, MemoryMap state) { - processAnyLocationWrite((ValueNode) checkpoint, state); - } - - private static void processWrite(WriteNode writeNode, MemoryMap state) { - if (writeNode.location().locationIdentity() == LocationNode.ANY_LOCATION) { - processAnyLocationWrite(writeNode, state); - } - state.lastMemorySnapshot.put(writeNode.location().locationIdentity(), writeNode); - } - - private static void processAnyLocationWrite(ValueNode modifiying, MemoryMap state) { - for (Map.Entry entry : state.lastMemorySnapshot.entrySet()) { - entry.setValue(modifiying); - } - state.lastMemorySnapshot.put(LocationNode.ANY_LOCATION, modifiying); - state.loops.clear(); - } - - private void processRead(ReadNode readNode, MemoryMap state) { - StructuredGraph graph = (StructuredGraph) readNode.graph(); - assert readNode.getNullCheck() == false; - Object locationIdentity = readNode.location().locationIdentity(); - ValueNode lastLocationAccess = getLastLocationAccessForRead(state, locationIdentity); - FloatingReadNode floatingRead = graph.unique(new FloatingReadNode(readNode.object(), readNode.location(), lastLocationAccess, readNode.stamp(), readNode.dependencies())); - floatingRead.setNullCheck(readNode.getNullCheck()); - ValueAnchorNode anchor = null; - for (GuardNode guard : readNode.dependencies().filter(GuardNode.class)) { - if (anchor == null) { - anchor = graph.add(new ValueAnchorNode()); - } - anchor.addAnchoredNode(guard); - } - if (anchor != null) { - graph.addAfterFixed(readNode, anchor); - } - graph.replaceFixedWithFloating(readNode, floatingRead); - } - - private ValueNode getLastLocationAccessForRead(MemoryMap state, Object locationIdentity) { - ValueNode lastLocationAccess; - if (locationIdentity == LocationNode.FINAL_LOCATION) { - lastLocationAccess = null; - } else { - lastLocationAccess = state.lastMemorySnapshot.get(locationIdentity); - if (lastLocationAccess == null) { - LoopState lastLoop = state.loops.peek(); - if (lastLoop == null) { - lastLocationAccess = state.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); - } else { - ValueNode phiInit; - if (state.loops.size() > 1) { - phiInit = getLastLocationAccessForRead(state.loops.get(1).state, locationIdentity); - } else { - phiInit = lastLoop.loopEntryAnyLocation; - } - PhiNode phi = lastLoop.loopBegin.graph().add(new PhiNode(PhiType.Memory, lastLoop.loopBegin)); - phi.addInput(phiInit); - lastLoop.state.lastMemorySnapshot.put(locationIdentity, phi); - lastLoop.loopPhiLocations.put(phi, locationIdentity); - tryFinishLoopPhis(lastLoop.state, lastLoop.loopBegin); - lastLocationAccess = phi; - } - state.lastMemorySnapshot.put(locationIdentity, lastLocationAccess); - } - } - return lastLocationAccess; - } - - private static void processLoopExit(LoopExitNode exit, MemoryMap state) { - for (Map.Entry entry : state.lastMemorySnapshot.entrySet()) { - entry.setValue(exit.graph().unique(new ValueProxyNode(entry.getValue(), exit, PhiType.Memory))); - } - if (!state.loops.isEmpty()) { - state.loops.pop(); - } - } - - private void tryFinishLoopPhis(MemoryMap loopMemory, LoopBeginNode loopBegin) { - List loopEndStates = loopEndStatesMap.get(loopBegin); - if (loopEndStates == null) { - return; - } - LoopState loopState = loopMemory.loops.get(0); - int i = 0; - while (loopState.loopBegin != loopBegin) { - loopState = loopMemory.loops.get(++i); - } - for (PhiNode phi : loopBegin.phis()) { - if (phi.type() == PhiType.Memory && phi.valueCount() == 1) { - Object location = loopState.loopPhiLocations.get(phi); - assert location != null : "unknown location for " + phi; - for (MemoryMap endState : loopEndStates) { - ValueNode otherNode = endState.lastMemorySnapshot.get(location); - if (otherNode == null) { - otherNode = endState.lastMemorySnapshot.get(LocationNode.ANY_LOCATION); - } - phi.addInput(otherNode); - } - } - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/GlobalValueNumberingPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/GlobalValueNumberingPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - -public class GlobalValueNumberingPhase extends Phase { - - public static final DebugMetric metricGlobalValueNumberingHits = Debug.metric("GlobalValueNumberingHits"); - - @Override - protected void run(StructuredGraph graph) { - NodeBitMap visited = graph.createNodeBitMap(); - for (Node n : graph.getNodes()) { - apply(n, visited, graph); - } - } - - private void apply(Node n, NodeBitMap visited, StructuredGraph compilerGraph) { - if (!visited.isMarked(n)) { - visited.mark(n); - for (Node input : n.inputs()) { - apply(input, visited, compilerGraph); - } - if (n.getNodeClass().valueNumberable()) { - Node newNode = compilerGraph.findDuplicate(n); - if (newNode != null) { - assert !(n instanceof FixedNode || newNode instanceof FixedNode); - n.replaceAtUsages(newNode); - n.safeDelete(); - metricGlobalValueNumberingHits.increment(); - Debug.log("GVN applied and new node is %1s", newNode); - } - } - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IdentifyBoxingPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IdentifyBoxingPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.lang.reflect.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; - -public class IdentifyBoxingPhase extends Phase { - - private final BoxingMethodPool pool; - - public IdentifyBoxingPhase(BoxingMethodPool pool) { - this.pool = pool; - } - - @Override - protected void run(StructuredGraph graph) { - for (Invoke invoke : graph.getInvokes()) { - tryIntrinsify(invoke); - } - } - - public void tryIntrinsify(Invoke invoke) { - if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - return; - } - MethodCallTargetNode callTarget = invoke.methodCallTarget(); - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - if (pool.isSpecialMethod(targetMethod)) { - assert callTarget.arguments().size() == 1 : "boxing/unboxing method must have exactly one argument"; - Kind returnKind = callTarget.returnKind(); - ValueNode sourceValue = callTarget.arguments().get(0); - - // Check whether this is a boxing or an unboxing. - Node newNode = null; - if (returnKind == Kind.Object) { - // We have a boxing method here. - assert Modifier.isStatic(targetMethod.accessFlags()) : "boxing method must be static"; - Kind sourceKind = targetMethod.signature().argumentKindAt(0); - newNode = invoke.graph().add(new BoxNode(sourceValue, targetMethod.holder(), sourceKind, invoke.bci())); - } else { - // We have an unboxing method here. - assert !Modifier.isStatic(targetMethod.accessFlags()) : "unboxing method must be an instance method"; - newNode = invoke.graph().add(new UnboxNode(returnKind, sourceValue)); - } - - // Intrinsify the invoke to the special node. - invoke.intrinsify(newNode); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/InliningPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/InliningPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,420 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.internal.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.PhasePlan.*; -import com.oracle.graal.phases.util.*; -import com.oracle.graal.phases.util.InliningUtil.*; - - -public class InliningPhase extends Phase implements InliningCallback { - /* - * - Detect method which only call another method with some parameters set to constants: void foo(a) -> void foo(a, b) -> void foo(a, b, c) ... - * These should not be taken into account when determining inlining depth. - * - honor the result of overrideInliningDecision(0, caller, invoke.bci, method, true); - */ - - private final TargetDescription target; - private final GraalCodeCacheProvider runtime; - - private final Collection hints; - - private final PriorityQueue inlineCandidates = new PriorityQueue<>(); - private Assumptions assumptions; - - private final PhasePlan plan; - private final GraphCache cache; - private final WeightComputationPolicy weightComputationPolicy; - private final InliningPolicy inliningPolicy; - private final OptimisticOptimizations optimisticOpts; - - // Metrics - private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); - private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); - private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); - - public InliningPhase(TargetDescription target, GraalCodeCacheProvider runtime, Collection hints, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) { - this.target = target; - this.runtime = runtime; - this.hints = hints; - this.assumptions = assumptions; - this.cache = cache; - this.plan = plan; - this.optimisticOpts = optimisticOpts; - this.weightComputationPolicy = createWeightComputationPolicy(); - this.inliningPolicy = createInliningPolicy(); - } - - @SuppressWarnings("unchecked") - @Override - protected void run(final StructuredGraph graph) { - graph.createNodeMap(); - - if (hints != null) { - scanInvokes((Iterable) Util.uncheckedCast(this.hints)); - } else { - scanInvokes(graph.getNodes(InvokeNode.class)); - scanInvokes(graph.getNodes(InvokeWithExceptionNode.class)); - } - - while (!inlineCandidates.isEmpty() && graph.getNodeCount() < GraalOptions.MaximumDesiredSize) { - InlineInfo candidate = inlineCandidates.remove(); - if (!candidate.invoke.node().isAlive()) { - continue; - } - // refresh infos - final InlineInfo info = InliningUtil.getInlineInfo(candidate.invoke, candidate.level, runtime, assumptions, this, optimisticOpts); - - boolean inline = Debug.scope("InliningDecisions", new Callable() { - @Override - public Boolean call() throws Exception { - return info != null && inliningPolicy.isWorthInlining(graph, info); - } - }); - - if (inline) { - int mark = graph.getMark(); - Iterable newNodes = null; - try { - info.inline(graph, runtime, this); - Debug.dump(graph, "after %s", info); - newNodes = graph.getNewNodes(mark); - if (GraalOptions.OptCanonicalizer) { - new CanonicalizerPhase(target, runtime, assumptions, mark, null).apply(graph); - } -// if (GraalOptions.Intrinsify) { -// new IntrinsificationPhase(runtime).apply(graph); -// } - metricInliningPerformed.increment(); - } catch (BailoutException bailout) { - // TODO determine if we should really bail out of the whole compilation. - throw bailout; - } catch (AssertionError e) { - throw new GraalInternalError(e).addContext(info.toString()); - } catch (RuntimeException e) { - throw new GraalInternalError(e).addContext(info.toString()); - } catch (GraalInternalError e) { - throw e.addContext(info.toString()); - } - - if (newNodes != null && info.level < GraalOptions.MaximumInlineLevel) { - scanInvokes(newNodes); - } - } - } - - if (GraalOptions.Debug && graph.getNodeCount() >= GraalOptions.MaximumDesiredSize) { - Debug.scope("InliningDecisions", new Runnable() { - public void run() { - for (InlineInfo info : inlineCandidates) { - Debug.log("not inlining %s because inlining cut off by MaximumDesiredSize", InliningUtil.methodName(info)); - } - } - }); - - metricInliningStoppedByMaxDesiredSize.increment(); - } - } - - private void scanInvokes(final Iterable nodes) { - Debug.scope("InliningDecisions", new Runnable() { - public void run() { - for (Node node : nodes) { - if (node != null) { - if (node instanceof Invoke) { - Invoke invoke = (Invoke) node; - scanInvoke(invoke); - } - for (Node usage : node.usages().filterInterface(Invoke.class).snapshot()) { - scanInvoke((Invoke) usage); - } - } - } - } - }); - } - - private void scanInvoke(Invoke invoke) { - InlineInfo info = InliningUtil.getInlineInfo(invoke, computeInliningLevel(invoke), runtime, assumptions, this, optimisticOpts); - if (info != null) { - metricInliningConsidered.increment(); - inlineCandidates.add(info); - } - } - - public static final Map parsedMethods = new HashMap<>(); - - - - private static final DebugMetric metricInliningRuns = Debug.metric("Runs"); - - @Override - public StructuredGraph buildGraph(final ResolvedJavaMethod method) { - metricInliningRuns.increment(); - if (GraalOptions.CacheGraphs && cache != null) { - StructuredGraph cachedGraph = cache.get(method); - if (cachedGraph != null) { - return cachedGraph; - } - } - StructuredGraph newGraph = new StructuredGraph(method); - if (plan != null) { - plan.runPhases(PhasePosition.AFTER_PARSING, newGraph); - } - assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; - - if (GraalOptions.ProbabilityAnalysis) { - new DeadCodeEliminationPhase().apply(newGraph); - new ComputeProbabilityPhase().apply(newGraph); - } - if (GraalOptions.OptCanonicalizer) { - new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph); - } - if (GraalOptions.Intrinsify) { - new IntrinsificationPhase(runtime).apply(newGraph); - } - if (GraalOptions.CullFrameStates) { - new CullFrameStatesPhase().apply(newGraph); - } - if (GraalOptions.CacheGraphs && cache != null) { - cache.put(newGraph); - } - return newGraph; - } - - @Override - public double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke) { - boolean preferred = hints != null && hints.contains(invoke); - return weightComputationPolicy.computeWeight(caller, method, invoke, preferred); - } - - public static int graphComplexity(StructuredGraph graph) { - int result = 0; - for (Node node : graph.getNodes()) { - if (node instanceof ConstantNode || node instanceof LocalNode || node instanceof BeginNode || node instanceof ReturnNode || node instanceof UnwindNode) { - result += 0; - } else if (node instanceof PhiNode) { - result += 5; - } else if (node instanceof MergeNode || node instanceof Invoke || node instanceof LoopEndNode || node instanceof EndNode) { - result += 0; - } else if (node instanceof ControlSplitNode) { - result += ((ControlSplitNode) node).blockSuccessorCount(); - } else { - result += 1; - } - } - return Math.max(1, result); - } - - - @Override - public void recordConcreteMethodAssumption(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) { - assumptions.recordConcreteMethod(method, context, impl); - } - - @Override - public void recordMethodContentsAssumption(ResolvedJavaMethod method) { - if (assumptions != null) { - assumptions.recordMethodContents(method); - } - } - - private static int computeInliningLevel(Invoke invoke) { - int count = -1; - FrameState curState = invoke.stateAfter(); - while (curState != null) { - count++; - curState = curState.outerFrameState(); - } - return count; - } - - private static InliningPolicy createInliningPolicy() { - switch(GraalOptions.InliningPolicy) { - case 0: return new WeightBasedInliningPolicy(); - case 1: return new C1StaticSizeBasedInliningPolicy(); - case 2: return new MinimumCodeSizeBasedInliningPolicy(); - case 3: return new DynamicSizeBasedInliningPolicy(); - case 4: return new GreedySizeBasedInliningPolicy(); - default: - GraalInternalError.shouldNotReachHere(); - return null; - } - } - - private static WeightComputationPolicy createWeightComputationPolicy() { - switch(GraalOptions.WeightComputationPolicy) { - case 0: throw new GraalInternalError("removed because of invokation counter changes"); - case 1: return new BytecodeSizeBasedWeightComputationPolicy(); - case 2: return new ComplexityBasedWeightComputationPolicy(); - default: - GraalInternalError.shouldNotReachHere(); - return null; - } - } - - private interface InliningPolicy { - boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info); - } - - private static class WeightBasedInliningPolicy implements InliningPolicy { - @Override - public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { - if (!checkCompiledCodeSize(info)) { - return false; - } - - double penalty = Math.pow(GraalOptions.InliningSizePenaltyExp, callerGraph.getNodeCount() / (double) GraalOptions.MaximumDesiredSize) / GraalOptions.InliningSizePenaltyExp; - if (info.weight > GraalOptions.MaximumInlineWeight / (1 + penalty * GraalOptions.InliningSizePenalty)) { - Debug.log("not inlining %s (cut off by weight %e)", InliningUtil.methodName(info), info.weight); - return false; - } - - Debug.log("inlining %s (weight %f): %s", InliningUtil.methodName(info), info.weight); - return true; - } - } - - private static class C1StaticSizeBasedInliningPolicy implements InliningPolicy { - @Override - public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { - double maxSize = Math.max(GraalOptions.MaximumTrivialSize, Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize); - return decideSizeBasedInlining(info, maxSize); - } - } - - private static class MinimumCodeSizeBasedInliningPolicy implements InliningPolicy { - @Override - public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { - assert GraalOptions.ProbabilityAnalysis; - if (!checkCompiledCodeSize(info)) { - return false; - } - - double inlineWeight = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()); - double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize * inlineWeight; - maxSize = Math.max(GraalOptions.MaximumTrivialSize, maxSize); - - return decideSizeBasedInlining(info, maxSize); - } - } - - private static class DynamicSizeBasedInliningPolicy implements InliningPolicy { - @Override - public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { - assert GraalOptions.ProbabilityAnalysis; - if (!checkCompiledCodeSize(info)) { - return false; - } - - double inlineBoost = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()) + Math.log10(Math.max(1, info.invoke.probability() - GraalOptions.ProbabilityCapForInlining + 1)); - double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize; - maxSize = maxSize + maxSize * inlineBoost; - maxSize = Math.min(GraalOptions.MaximumGreedyInlineSize, Math.max(GraalOptions.MaximumTrivialSize, maxSize)); - - return decideSizeBasedInlining(info, maxSize); - } - } - - private static class GreedySizeBasedInliningPolicy implements InliningPolicy { - @Override - public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { - assert GraalOptions.ProbabilityAnalysis; - if (!checkCompiledCodeSize(info)) { - return false; - } - - double maxSize = GraalOptions.MaximumGreedyInlineSize; - if (GraalOptions.InliningBonusPerTransferredValue != 0) { - Signature signature = info.invoke.methodCallTarget().targetMethod().signature(); - int transferredValues = signature.argumentCount(!Modifier.isStatic(info.invoke.methodCallTarget().targetMethod().accessFlags())); - if (signature.returnKind() != Kind.Void) { - transferredValues++; - } - maxSize += transferredValues * GraalOptions.InliningBonusPerTransferredValue; - } - - double inlineRatio = Math.min(GraalOptions.ProbabilityCapForInlining, info.invoke.probability()); - maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * maxSize * inlineRatio; - maxSize = Math.max(maxSize, GraalOptions.MaximumTrivialSize); - - return decideSizeBasedInlining(info, maxSize); - } - } - - private static boolean decideSizeBasedInlining(InlineInfo info, double maxSize) { - boolean success = info.weight <= maxSize; - if (DebugScope.getInstance().isLogEnabled()) { - String formatterString = success ? "inlining %s (size %f <= %f)" : "not inlining %s (too large %f > %f)"; - Debug.log(formatterString, InliningUtil.methodName(info), info.weight, maxSize); - } - return success; - } - - private static boolean checkCompiledCodeSize(InlineInfo info) { - if (GraalOptions.SmallCompiledCodeSize >= 0 && info.compiledCodeSize() > GraalOptions.SmallCompiledCodeSize) { - Debug.log("not inlining %s (CompiledCodeSize %d > %d)", InliningUtil.methodName(info), info.compiledCodeSize(), GraalOptions.SmallCompiledCodeSize); - return false; - } - return true; - } - - - private interface WeightComputationPolicy { - double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke); - } - - private static class BytecodeSizeBasedWeightComputationPolicy implements WeightComputationPolicy { - @Override - public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { - double codeSize = method.codeSize(); - if (preferredInvoke) { - codeSize = codeSize / GraalOptions.BoostInliningForEscapeAnalysis; - } - return codeSize; - } - } - - private static class ComplexityBasedWeightComputationPolicy implements WeightComputationPolicy { - @Override - public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { - double complexity = method.compilationComplexity(); - if (preferredInvoke) { - complexity = complexity / GraalOptions.BoostInliningForEscapeAnalysis; - } - return complexity; - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/InsertStateAfterPlaceholderPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/InsertStateAfterPlaceholderPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; - -public class InsertStateAfterPlaceholderPhase extends Phase { - - private static class PlaceholderNode extends AbstractStateSplit implements StateSplit, Node.IterableNodeType, LIRLowerable, Canonicalizable { - public PlaceholderNode() { - super(StampFactory.forVoid()); - } - - @Override - public void generate(LIRGeneratorTool gen) { - // nothing to do - } - - @Override - public boolean hasSideEffect(CodeCacheProvider runtime) { - return false; - } - - @Override - public ValueNode canonical(CanonicalizerTool tool) { - if (stateAfter() == null) { - return null; - } - return this; - } - } - - @Override - protected void run(StructuredGraph graph) { - for (ReturnNode ret : graph.getNodes(ReturnNode.class)) { - PlaceholderNode p = graph.add(new PlaceholderNode()); - p.setStateAfter(graph.add(new FrameState(FrameState.AFTER_BCI))); - graph.addBeforeFixed(ret, p); - } - } - -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IntrinsificationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IntrinsificationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.util.*; - -public class IntrinsificationPhase extends Phase { - - private final GraalCodeCacheProvider runtime; - - public IntrinsificationPhase(GraalCodeCacheProvider runtime) { - this.runtime = runtime; - } - - @Override - protected void run(StructuredGraph graph) { - for (InvokeNode invoke : graph.getNodes(InvokeNode.class)) { - tryIntrinsify(invoke, runtime); - } - for (InvokeWithExceptionNode invoke : graph.getNodes(InvokeWithExceptionNode.class)) { - tryIntrinsify(invoke, runtime); - } - } - - public static boolean canIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { - return getIntrinsicGraph(invoke, target, runtime) != null; - } - - private static void tryIntrinsify(Invoke invoke, GraalCodeCacheProvider runtime) { - if (invoke.callTarget() instanceof MethodCallTargetNode && invoke.methodCallTarget().targetMethod() != null) { - tryIntrinsify(invoke, invoke.methodCallTarget().targetMethod(), runtime); - } - } - - private static void tryIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { - StructuredGraph intrinsicGraph = getIntrinsicGraph(invoke, target, runtime); - if (intrinsicGraph != null) { - Debug.log(" > Intrinsify %s", target); - InliningUtil.inline(invoke, intrinsicGraph, true); - } - } - - private static StructuredGraph getIntrinsicGraph(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { - StructuredGraph intrinsicGraph = (StructuredGraph) target.compilerStorage().get(Graph.class); - if (intrinsicGraph == null) { - // TODO remove once all intrinsics are available via compilerStorage - intrinsicGraph = runtime.intrinsicGraph(invoke.stateAfter().method(), invoke.bci(), target, invoke.callTarget().arguments()); - } - return intrinsicGraph; - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IterativeCheckCastEliminationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/IterativeCheckCastEliminationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.graph.Graph.InputChangedListener; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - - -public class IterativeCheckCastEliminationPhase extends Phase { - private final TargetDescription target; - private final MetaAccessProvider runtime; - private final Assumptions assumptions; - - public IterativeCheckCastEliminationPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) { - this.target = target; - this.runtime = runtime; - this.assumptions = assumptions; - } - - @Override - protected void run(StructuredGraph graph) { - Set canonicalizationRoots = new HashSet<>(); - CheckCastEliminationPhase eliminate = new CheckCastEliminationPhase(); - Listener listener = new Listener(canonicalizationRoots); - while (true) { - graph.trackInputChange(listener); - eliminate.apply(graph); - graph.stopTrackingInputChange(); - if (canonicalizationRoots.isEmpty()) { - break; - } - new CanonicalizerPhase(target, runtime, assumptions, canonicalizationRoots, null).apply(graph); - canonicalizationRoots.clear(); - } - } - - private static class Listener implements InputChangedListener { - private final Set canonicalizationRoots; - public Listener(Set canonicalizationRoots) { - this.canonicalizationRoots = canonicalizationRoots; - } - @Override - public void inputChanged(Node node) { - canonicalizationRoots.add(node); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/LoopSafepointInsertionPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/LoopSafepointInsertionPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.graph.iterators.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.util.*; - -/** - * Adds safepoints to loops. - */ -public class LoopSafepointInsertionPhase extends Phase { - - @Override - protected void run(StructuredGraph graph) { - nextLoop: - for (LoopEndNode loopEnd : graph.getNodes(LoopEndNode.class)) { - if (!loopEnd.canSafepoint()) { - continue; - } - if (GraalOptions.OptSafepointElimination) { - // We 'eliminate' safepoints by simply never placing them into loops that have at least one call - NodeIterable it = NodeIterators.dominators(loopEnd).until(loopEnd.loopBegin()); - for (FixedNode n : it) { - if (n instanceof Invoke) { - continue nextLoop; - } - } - } - SafepointNode safepoint = graph.add(new SafepointNode()); - graph.addBeforeFixed(loopEnd, safepoint); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/LoweringPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/LoweringPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.iterators.*; -import com.oracle.graal.lir.cfg.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.schedule.*; - -/** - * Processes all {@link Lowerable} nodes to do their lowering. - */ -public class LoweringPhase extends Phase { - - final class LoweringToolImpl implements LoweringTool { - - final FixedNode guardAnchor; - final NodeBitMap activeGuards; - FixedWithNextNode lastFixedNode; - - public LoweringToolImpl(FixedNode guardAnchor, NodeBitMap activeGuards) { - this.guardAnchor = guardAnchor; - this.activeGuards = activeGuards; - } - - @Override - public GraalCodeCacheProvider getRuntime() { - return runtime; - } - - @Override - public ValueNode createNullCheckGuard(ValueNode object, long leafGraphId) { - return createGuard(object.graph().unique(new IsNullNode(object)), DeoptimizationReason.NullCheckException, DeoptimizationAction.InvalidateReprofile, true, leafGraphId); - } - - @Override - public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, long leafGraphId) { - return createGuard(condition, deoptReason, action, false, leafGraphId); - } - - @Override - public Assumptions assumptions() { - return assumptions; - } - - @Override - public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, boolean negated, long leafGraphId) { - if (GraalOptions.OptEliminateGuards) { - for (Node usage : condition.usages()) { - if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage)) { - return (ValueNode) usage; - } - } - } - GuardNode newGuard = guardAnchor.graph().unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, leafGraphId)); - if (GraalOptions.OptEliminateGuards) { - activeGuards.grow(); - activeGuards.mark(newGuard); - } - return newGuard; - } - - public FixedWithNextNode lastFixedNode() { - return lastFixedNode; - } - } - - private final GraalCodeCacheProvider runtime; - private final Assumptions assumptions; - - public LoweringPhase(GraalCodeCacheProvider runtime, Assumptions assumptions) { - this.runtime = runtime; - this.assumptions = assumptions; - } - - private static boolean containsLowerable(NodeIterable nodes) { - for (Node n : nodes) { - if (n instanceof Lowerable) { - return true; - } - } - return false; - } - - @Override - protected void run(final StructuredGraph graph) { - int i = 0; - NodeBitMap processed = graph.createNodeBitMap(); - while (true) { - int mark = graph.getMark(); - final SchedulePhase schedule = new SchedulePhase(); - schedule.apply(graph, false); - - processBlock(schedule.getCFG().getStartBlock(), graph.createNodeBitMap(), null, schedule, processed); - Debug.dump(graph, "Lowering iteration %d", i++); - new CanonicalizerPhase(null, runtime, assumptions, mark, null).apply(graph); - - if (!containsLowerable(graph.getNewNodes(mark))) { - // No new lowerable nodes - done! - break; - } - assert graph.verify(); - processed.grow(); - } - } - - private void processBlock(Block block, NodeBitMap activeGuards, FixedNode parentAnchor, SchedulePhase schedule, NodeBitMap processed) { - - FixedNode anchor = parentAnchor; - if (anchor == null) { - anchor = block.getBeginNode(); - } - process(block, activeGuards, anchor, schedule, processed); - - // Process always reached block first. - Block alwaysReachedBlock = block.getPostdominator(); - if (alwaysReachedBlock != null && alwaysReachedBlock.getDominator() == block) { - processBlock(alwaysReachedBlock, activeGuards, anchor, schedule, processed); - } - - // Now go for the other dominators. - for (Block dominated : block.getDominated()) { - if (dominated != alwaysReachedBlock) { - assert dominated.getDominator() == block; - processBlock(dominated, activeGuards, null, schedule, processed); - } - } - - if (parentAnchor == null && GraalOptions.OptEliminateGuards) { - for (GuardNode guard : anchor.usages().filter(GuardNode.class)) { - activeGuards.clear(guard); - } - } - } - - private void process(final Block b, final NodeBitMap activeGuards, final FixedNode anchor, SchedulePhase schedule, NodeBitMap processed) { - - final LoweringToolImpl loweringTool = new LoweringToolImpl(anchor, activeGuards); - - // Lower the instructions of this block. - List nodes = schedule.nodesFor(b); - - for (Node node : nodes) { - FixedNode lastFixedNext = null; - if (node instanceof FixedWithNextNode) { - FixedWithNextNode fixed = (FixedWithNextNode) node; - lastFixedNext = fixed.next(); - loweringTool.lastFixedNode = fixed; - } - - if (node.isAlive() && !processed.isMarked(node)) { - processed.mark(node); - if (node instanceof Lowerable) { - ((Lowerable) node).lower(loweringTool); - } - } - - if (loweringTool.lastFixedNode == node && !node.isAlive()) { - if (lastFixedNext == null) { - loweringTool.lastFixedNode = null; - } else { - Node prev = lastFixedNext.predecessor(); - if (prev != node && prev instanceof FixedWithNextNode) { - loweringTool.lastFixedNode = (FixedWithNextNode) prev; - } else if (lastFixedNext instanceof FixedWithNextNode) { - loweringTool.lastFixedNode = (FixedWithNextNode) lastFixedNext; - } else { - loweringTool.lastFixedNode = null; - } - } - } - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/Phase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/Phase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.debug.*; -import com.oracle.graal.nodes.*; - -public abstract class Phase { - - private String name; - private static final DebugMetric metricPhaseRuns = Debug.metric("Runs"); - protected static final DebugMetric METRIC_PROCESSED_NODES = Debug.metric("ProcessedNodes"); - - protected Phase() { - this.name = this.getClass().getSimpleName(); - if (name.endsWith("Phase")) { - name = name.substring(0, name.length() - "Phase".length()); - } - } - - protected Phase(String name) { - this.name = name; - } - - protected String getDetailedName() { - return getName(); - } - - public final void apply(final StructuredGraph graph) { - apply(graph, true); - } - - public final void apply(final StructuredGraph graph, final boolean dumpGraph) { - Debug.scope(name, this, new Runnable() { - public void run() { - Phase.this.run(graph); - metricPhaseRuns.increment(); - if (dumpGraph) { - Debug.dump(graph, "After phase %s", name); - } - assert graph.verify(); - } - }); - } - - public final String getName() { - return name; - } - - protected abstract void run(StructuredGraph graph); -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/PhasePlan.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/PhasePlan.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.nodes.*; - -/** - * Tells the compiler about additional phases that need to be executed during compilation. - */ -public class PhasePlan { - /** - * The compilation is split into the following sections: - * ======================================================================== - * Period 1: High-level nodes. (Graph building) - * ======================================================================== - * Runtime-specific lowering. - * ======================================================================== - * Period 2: Mid-level nodes. (Memory dependence graph) - * ======================================================================== - * Target-specific lowering, de-SSA. - * ======================================================================== - * Period 3: Low-level nodes. (Register allocation, code generation) - * ======================================================================== - * - * A compiler extension phase can chose to run at the end of periods 1-3. - */ - public static enum PhasePosition { - AFTER_PARSING, - HIGH_LEVEL, - MID_LEVEL, - LOW_LEVEL - } - - @SuppressWarnings("unchecked") - private final ArrayList[] phases = new ArrayList[PhasePosition.values().length]; - private final Set> disabledPhases = new HashSet<>(); - - public void addPhase(PhasePosition pos, Phase phase) { - if (phases[pos.ordinal()] == null) { - phases[pos.ordinal()] = new ArrayList<>(); - } - phases[pos.ordinal()].add(phase); - } - - public void runPhases(PhasePosition pos, StructuredGraph graph) { - if (phases[pos.ordinal()] != null) { - for (Phase p : phases[pos.ordinal()]) { - p.apply(graph); - } - } - } - - public void disablePhase(Class clazz) { - disabledPhases.add(clazz); - } - - public boolean isPhaseDisabled(Class clazz) { - return disabledPhases.contains(clazz); - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/PhiStampPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/PhiStampPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.nodes.*; - -public class PhiStampPhase extends Phase { - @Override - protected void run(StructuredGraph graph) { - // Infer phis stopping at loop phis. - for (PhiNode phi : graph.getNodes(PhiNode.class)) { - inferPhi(phi); - } - - // Start iterative inference for loop phis. - if (graph.hasLoops()) { - for (PhiNode phi : graph.getNodes(PhiNode.class)) { - if (phi.isLoopPhi()) { - iterativeInferPhi(phi); - } - } - } - } - - private void iterativeInferPhi(PhiNode phi) { - if (phi.inferPhiStamp()) { - for (PhiNode phiUsage : phi.usages().filter(PhiNode.class)) { - iterativeInferPhi(phiUsage); - } - } - } - - private void inferPhi(PhiNode phi) { - for (PhiNode phiInput : phi.values().filter(PhiNode.class)) { - if (!phiInput.isLoopPhi()) { - inferPhi(phiInput); - } - } - phi.inferPhiStamp(); - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ReadEliminationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/ReadEliminationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.PhiNode.PhiType; -import com.oracle.graal.nodes.extended.*; - -public class ReadEliminationPhase extends Phase { - private Queue newPhis; - - @Override - protected void run(StructuredGraph graph) { - newPhis = new LinkedList<>(); - for (FloatingReadNode n : graph.getNodes(FloatingReadNode.class)) { - if (isReadEliminable(n)) { - NodeMap nodeMap = n.graph().createNodeMap(); - ValueNode value = getValue(n, n.lastLocationAccess(), nodeMap); - Debug.log("Eliminated memory read %1.1s and replaced with node %s", n, value); - graph.replaceFloating(n, value); - } - } - } - - private boolean isReadEliminable(FloatingReadNode n) { - return isWrites(n, n.lastLocationAccess(), n.graph().createNodeBitMap()); - } - - private boolean isWrites(FloatingReadNode n, Node lastLocationAccess, NodeBitMap visited) { - if (lastLocationAccess == null) { - return false; - } - if (visited.isMarked(lastLocationAccess)) { - return true; // dataflow loops must come from Phis assume them ok until proven wrong - } - if (lastLocationAccess instanceof ValueProxyNode) { - return isWrites(n, ((ValueProxyNode) lastLocationAccess).value(), visited); - } - if (lastLocationAccess instanceof WriteNode) { - WriteNode other = (WriteNode) lastLocationAccess; - return other.object() == n.object() && other.location() == n.location(); - } - if (lastLocationAccess instanceof PhiNode) { - visited.mark(lastLocationAccess); - for (ValueNode value : ((PhiNode) lastLocationAccess).values()) { - if (!isWrites(n, value, visited)) { - return false; - } - } - return true; - } - return false; - } - - private ValueNode getValue(FloatingReadNode n, Node lastLocationAccess, NodeMap nodeMap) { - ValueNode exisiting = nodeMap.get(lastLocationAccess); - if (exisiting != null) { - return exisiting; - } - if (lastLocationAccess instanceof ValueProxyNode) { - ValueProxyNode proxy = (ValueProxyNode) lastLocationAccess; - ValueNode value = getValue(n, proxy.value(), nodeMap); - return lastLocationAccess.graph().add(new ValueProxyNode(value, proxy.proxyPoint(), PhiType.Value)); - } - if (lastLocationAccess instanceof WriteNode) { - return ((WriteNode) lastLocationAccess).value(); - } - if (lastLocationAccess instanceof PhiNode) { - PhiNode phi = (PhiNode) lastLocationAccess; - PhiNode newPhi = phi.graph().add(new PhiNode(n.kind(), phi.merge())); - nodeMap.set(lastLocationAccess, newPhi); - for (ValueNode value : phi.values()) { - newPhi.addInput(getValue(n, value, nodeMap)); - } - newPhis.add(newPhi); - return newPhi; - } - throw GraalInternalError.shouldNotReachHere(); - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/RemoveValueProxyPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/RemoveValueProxyPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import com.oracle.graal.nodes.*; - -public class RemoveValueProxyPhase extends Phase { - - @Override - protected void run(StructuredGraph graph) { - for (ValueProxyNode vpn : graph.getNodes(ValueProxyNode.class)) { - graph.replaceFloating(vpn, vpn.value()); - } - for (LoopExitNode exit : graph.getNodes(LoopExitNode.class)) { - FrameState stateAfter = exit.stateAfter(); - if (stateAfter != null) { - exit.setStateAfter(null); - if (stateAfter.usages().count() == 0) { - stateAfter.safeDelete(); - } - } - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/TailDuplicationPhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/phases/TailDuplicationPhase.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,551 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.phases; - -import java.util.*; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.Graph.DuplicationReplacement; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.VirtualState.NodeClosure; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; - -/** - * This class is a phase that looks for opportunities for tail duplication. The static method - * {@link #tailDuplicate(MergeNode, TailDuplicationDecision, List)} can also be used to drive tail duplication from - * other places, e.g., inlining. - */ -public class TailDuplicationPhase extends Phase { - - /* - * Various metrics on the circumstances in which tail duplication was/wasn't performed. - */ - private static final DebugMetric metricDuplicationMonitors = Debug.metric("DuplicationMonitors"); - private static final DebugMetric metricDuplicationEnd = Debug.metric("DuplicationEnd"); - private static final DebugMetric metricDuplicationEndPerformed = Debug.metric("DuplicationEndPerformed"); - private static final DebugMetric metricDuplicationOther = Debug.metric("DuplicationOther"); - private static final DebugMetric metricDuplicationOtherPerformed = Debug.metric("DuplicationOtherPerformed"); - - /** - * This interface is used by tail duplication to let clients decide if tail duplication should be performed. - */ - public interface TailDuplicationDecision { - - /** - * Queries if tail duplication should be performed at the given merge. If this method returns true then the tail - * duplication will be performed, because all other checks have happened before. - * - * @param merge The merge at which tail duplication can be performed. - * @param fixedNodeCount The size of the set of fixed nodes that forms the base for the duplicated set of nodes. - * @return true if the tail duplication should be performed, false otherwise. - */ - boolean doTransform(MergeNode merge, int fixedNodeCount); - } - - /** - * A tail duplication decision closure that employs the default algorithm: Check if there are any phis on the merge - * whose stamps improve and that have usages within the duplicated set of fixed nodes. - */ - public static final TailDuplicationDecision DEFAULT_DECISION = new TailDuplicationDecision() { - - public boolean doTransform(MergeNode merge, int fixedNodeCount) { - if (fixedNodeCount < GraalOptions.TailDuplicationTrivialSize) { - return true; - } - HashSet improvements = new HashSet<>(); - for (PhiNode phi : merge.phis()) { - Stamp phiStamp = phi.stamp(); - for (ValueNode input : phi.values()) { - if (!input.stamp().equals(phiStamp)) { - improvements.add(phi); - break; - } - } - } - if (improvements.isEmpty()) { - return false; - } - FixedNode current = merge; - int opportunities = 0; - while (current instanceof FixedWithNextNode) { - current = ((FixedWithNextNode) current).next(); - for (PhiNode phi : improvements) { - for (Node input : current.inputs()) { - if (input == phi) { - opportunities++; - } - if (input.inputs().contains(phi)) { - opportunities++; - } - } - } - } - return opportunities > 0; - } - }; - - /** - * A tail duplication decision closure that always returns true. - */ - public static final TailDuplicationDecision TRUE_DECISION = new TailDuplicationDecision() { - - @Override - public boolean doTransform(MergeNode merge, int fixedNodeCount) { - return true; - } - }; - - @Override - protected void run(StructuredGraph graph) { - // A snapshot is taken here, so that new MergeNode instances aren't considered for tail duplication. - for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) { - if (!(merge instanceof LoopBeginNode) && merge.probability() >= GraalOptions.TailDuplicationProbability) { - tailDuplicate(merge, DEFAULT_DECISION, null); - } - } - } - - /** - * This method attempts to duplicate the tail of the given merge. The merge must not be a {@link LoopBeginNode}. If - * the merge is eligible for duplication (at least one fixed node in its tail, no {@link MonitorEnterNode}/ - * {@link MonitorExitNode}, non-null {@link MergeNode#stateAfter()}) then the decision callback is used to determine - * whether the tail duplication should actually be performed. If replacements is non-null, then this list of - * {@link PiNode}s is used to replace one value per merge end. - * - * @param merge The merge whose tail should be duplicated. - * @param decision A callback that can make the final decision if tail duplication should occur or not. - * @param replacements A list of {@link PiNode}s, or null. If this list is non-null then its size needs to match the - * merge's end count. Each entry can either be null or a {@link PiNode}, and is used to replace - * {@link PiNode#object()} with the {@link PiNode} in the duplicated branch that corresponds to the - * entry. - */ - public static void tailDuplicate(MergeNode merge, TailDuplicationDecision decision, List replacements) { - assert !(merge instanceof LoopBeginNode); - assert replacements == null || replacements.size() == merge.forwardEndCount(); - FixedNode fixed = merge; - int fixedCount = 0; - boolean containsMonitor = false; - while (fixed instanceof FixedWithNextNode) { - if (fixed instanceof MonitorEnterNode || fixed instanceof MonitorExitNode) { - containsMonitor = true; - } - fixed = ((FixedWithNextNode) fixed).next(); - fixedCount++; - } - if (containsMonitor) { - // cannot currently be handled - // TODO (ls) re-evaluate this limitation after changes to the lock representation and the LIR generator - metricDuplicationMonitors.increment(); - } else if (fixedCount > 1) { - if (fixed instanceof EndNode && !(((EndNode) fixed).merge() instanceof LoopBeginNode)) { - metricDuplicationEnd.increment(); - if (decision.doTransform(merge, fixedCount)) { - metricDuplicationEndPerformed.increment(); - new DuplicationOperation(merge, replacements).duplicate(); - } - } else if (merge.stateAfter() != null) { - metricDuplicationOther.increment(); - if (decision.doTransform(merge, fixedCount)) { - metricDuplicationOtherPerformed.increment(); - new DuplicationOperation(merge, replacements).duplicate(); - } - } - } - } - - /** - * This class encapsulates one tail duplication operation on a specific {@link MergeNode}. - */ - private static class DuplicationOperation { - - private final MergeNode merge; - private final StructuredGraph graph; - - private final HashMap bottomPhis = new HashMap<>(); - private final List replacements; - - /** - * Initializes the tail duplication operation without actually performing any work. - * - * @param merge The merge whose tail should be duplicated. - * @param replacements A list of replacement {@link PiNode}s, or null. If this is non-null, then the size of the - * list needs to match the number of end nodes at the merge. - */ - public DuplicationOperation(MergeNode merge, List replacements) { - this.merge = merge; - this.replacements = replacements; - this.graph = (StructuredGraph) merge.graph(); - } - - /** - * Performs the actual tail duplication: - *
    - *
  • Creates a new {@link ValueAnchorNode} at the beginning of the duplicated area, an transfers all - * dependencies from the merge to this anchor.
  • - *
  • Determines the set of fixed nodes to be duplicated.
  • - *
  • Creates the new merge at the bottom of the duplicated area.
  • - *
  • Determines the complete set of duplicated nodes.
  • - *
  • Performs the actual duplication.
  • - *
- */ - private void duplicate() { - Debug.log("tail duplication at merge %s in %s (prob %f)", merge, graph.method(), merge.probability()); - - ValueAnchorNode anchor = addValueAnchor(); - - // determine the fixed nodes that should be duplicated (currently: all nodes up until the first control - // split, end node, deopt or return. - ArrayList fixedNodes = new ArrayList<>(); - FixedNode fixed = merge.next(); - FrameState stateAfter = merge.stateAfter(); - while (fixed instanceof FixedWithNextNode) { - fixedNodes.add(fixed); - if (fixed instanceof StateSplit && ((StateSplit) fixed).stateAfter() != null) { - stateAfter = ((StateSplit) fixed).stateAfter(); - } - fixed = ((FixedWithNextNode) fixed).next(); - } - - EndNode endAfter = createNewMerge(fixed, stateAfter); - MergeNode mergeAfter = endAfter.merge(); - fixedNodes.add(endAfter); - final HashSet duplicatedNodes = buildDuplicatedNodeSet(fixedNodes, stateAfter); - mergeAfter.clearEnds(); - expandDuplicated(duplicatedNodes, mergeAfter); - retargetDependencies(duplicatedNodes, anchor); - - List endSnapshot = merge.forwardEnds().snapshot(); - List phiSnapshot = merge.phis().snapshot(); - - int endIndex = 0; - for (final EndNode forwardEnd : merge.forwardEnds()) { - Map duplicates; - if (replacements == null || replacements.get(endIndex) == null) { - duplicates = graph.addDuplicates(duplicatedNodes, (DuplicationReplacement) null); - } else { - HashMap replace = new HashMap<>(); - replace.put(replacements.get(endIndex).object(), replacements.get(endIndex)); - duplicates = graph.addDuplicates(duplicatedNodes, replace); - } - for (Map.Entry phi : bottomPhis.entrySet()) { - phi.getValue().initializeValueAt(merge.forwardEndIndex(forwardEnd), (ValueNode) duplicates.get(phi.getKey())); - } - mergeAfter.addForwardEnd((EndNode) duplicates.get(endAfter)); - - // re-wire the duplicated ValueAnchorNode to the predecessor of the corresponding EndNode - FixedNode anchorDuplicate = (FixedNode) duplicates.get(anchor); - ((FixedWithNextNode) forwardEnd.predecessor()).setNext(anchorDuplicate); - // move dependencies on the ValueAnchorNode to the previous BeginNode - BeginNode prevBegin = BeginNode.prevBegin(anchorDuplicate); - anchorDuplicate.replaceAtUsages(prevBegin); - - // re-wire the phi duplicates to the correct input - for (PhiNode phi : phiSnapshot) { - PhiNode phiDuplicate = (PhiNode) duplicates.get(phi); - for (Node usage : phiDuplicate.usages()) { - if (usage instanceof ValueNode) { - ((ValueNode) usage).dependencies().add(prevBegin); - } - } - phiDuplicate.replaceAtUsages(phi.valueAt(forwardEnd)); - phiDuplicate.safeDelete(); - } - endIndex++; - } - GraphUtil.killCFG(merge); - for (EndNode forwardEnd : endSnapshot) { - forwardEnd.safeDelete(); - } - for (PhiNode phi : phiSnapshot) { - // these phis should go away, but they still need to be anchored to a merge to be valid... - if (phi.isAlive()) { - phi.setMerge(mergeAfter); - } - } - Debug.dump(graph, "After tail duplication at %s", merge); - } - - /** - * Inserts a new ValueAnchorNode after the merge and transfers all dependency-usages (not phis) to this - * ValueAnchorNode. - * - * @return The new {@link ValueAnchorNode} that was created. - */ - private ValueAnchorNode addValueAnchor() { - ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); - graph.addAfterFixed(merge, anchor); - for (Node usage : merge.usages().snapshot()) { - if (usage instanceof PhiNode && ((PhiNode) usage).merge() == merge) { - // nothing to do - } else { - usage.replaceFirstInput(merge, anchor); - } - } - return anchor; - } - - /** - * Given a set of fixed nodes, this method determines the set of fixed and floating nodes that needs to be - * duplicated, i.e., all nodes that due to data flow and other dependencies needs to be duplicated. - * - * @param fixedNodes The set of fixed nodes that should be duplicated. - * @param stateAfter The frame state of the merge that follows the set of fixed nodes. All {@link ValueNode}s - * reachable from this state are considered to be reachable from within the duplicated set of nodes. - * @return The set of nodes that need to be duplicated. - */ - private HashSet buildDuplicatedNodeSet(final ArrayList fixedNodes, FrameState stateAfter) { - final NodeBitMap aboveBound = graph.createNodeBitMap(); - final NodeBitMap belowBound = graph.createNodeBitMap(); - - final Deque worklist = new ArrayDeque<>(); - - // Build the set of nodes that have (transitive) usages within the duplicatedNodes. - // This is achieved by iterating all nodes that are reachable via inputs from the the fixed nodes. - aboveBound.markAll(fixedNodes); - worklist.addAll(fixedNodes); - - // the phis at the original merge should always be duplicated - for (PhiNode phi : merge.phis()) { - aboveBound.mark(phi); - worklist.add(phi); - } - - NodeClosure aboveClosure = new NodeClosure() { - - @Override - public void apply(Node usage, Node node) { - if (node instanceof PhiNode && !fixedNodes.contains(((PhiNode) node).merge())) { - // stop iterating: phis belonging to outside merges are known to be outside. - } else if (node instanceof FixedNode) { - // stop iterating: fixed nodes within the given set are traversal roots anyway, and all other - // fixed nodes are known to be outside. - } else if (!aboveBound.isMarked(node)) { - worklist.add(node); - aboveBound.mark(node); - } - } - }; - - if (stateAfter != null) { - stateAfter.applyToNonVirtual(aboveClosure); - } - while (!worklist.isEmpty()) { - Node current = worklist.remove(); - for (Node input : current.inputs()) { - aboveClosure.apply(current, input); - } - } - - // Build the set of nodes that have (transitive) inputs within the duplicatedNodes. - // This is achieved by iterating all nodes that are reachable via usages from the fixed nodes. - belowBound.markAll(fixedNodes); - worklist.addAll(fixedNodes); - - // the phis at the original merge should always be duplicated - for (PhiNode phi : merge.phis()) { - belowBound.mark(phi); - worklist.add(phi); - } - - while (!worklist.isEmpty()) { - Node current = worklist.remove(); - for (Node usage : current.usages()) { - if (usage instanceof PhiNode && !fixedNodes.contains(((PhiNode) usage).merge())) { - // stop iterating: phis belonging to outside merges are known to be outside. - } else if (usage instanceof FixedNode) { - // stop iterating: fixed nodes within the given set are traversal roots anyway, and all other - // fixed nodes are known to be outside. - } else if (!belowBound.isMarked(usage)) { - worklist.add(usage); - belowBound.mark(usage); - } - } - } - - // build the intersection - belowBound.intersect(aboveBound); - HashSet result = new HashSet<>(); - for (Node node : belowBound) { - result.add(node); - } - return result; - } - - /** - * Creates a new merge and end node construct at the end of the duplicated area. While it is useless in itself - * (merge with only one end) it simplifies the later duplication step. - * - * @param successor The successor of the duplicated set of nodes, i.e., the first node that should not be - * duplicated. - * @param stateAfterMerge The frame state that should be used for the merge. - * @return The newly created end node. - */ - private EndNode createNewMerge(FixedNode successor, FrameState stateAfterMerge) { - MergeNode newBottomMerge = graph.add(new MergeNode()); - newBottomMerge.setProbability(successor.probability()); - EndNode newBottomEnd = graph.add(new EndNode()); - newBottomMerge.addForwardEnd(newBottomEnd); - newBottomMerge.setStateAfter(stateAfterMerge); - ((FixedWithNextNode) successor.predecessor()).setNext(newBottomEnd); - newBottomMerge.setNext(successor); - return newBottomEnd; - } - - /** - * Expands the set of nodes to be duplicated by looking at a number of conditions: - *
    - *
  • {@link ValueNode}s that have usages on the outside need to be replaced with phis for the outside usages.
  • - *
  • Non-{@link ValueNode} nodes that have outside usages (frame states, virtual object states, ...) need to - * be cloned immediately for the outside usages.
  • - *
  • Nodes that have a {@link StampFactory#extension()} or {@link StampFactory#condition()} stamp need to be - * cloned immediately for the outside usages.
  • - *
  • Dependencies into the duplicated nodes will be replaced with dependencies on the merge.
  • - *
  • Outside non-{@link ValueNode}s with usages within the duplicated set of nodes need to also be duplicated. - *
  • - *
  • Outside {@link ValueNode}s with {@link StampFactory#extension()} or {@link StampFactory#condition()} - * stamps that have usages within the duplicated set of nodes need to also be duplicated.
  • - *
- * - * @param duplicatedNodes The set of duplicated nodes that will be modified (expanded). - * @param newBottomMerge The merge that follows the duplicated set of nodes. It will be used for newly created - * phis and to as a target for dependencies that pointed into the duplicated set of nodes. - */ - private void expandDuplicated(HashSet duplicatedNodes, MergeNode newBottomMerge) { - Deque worklist = new ArrayDeque<>(duplicatedNodes); - - while (!worklist.isEmpty()) { - Node duplicated = worklist.remove(); - if (hasUsagesOutside(duplicated, duplicatedNodes)) { - boolean cloneForOutsideUsages = false; - if (duplicated instanceof ValueNode) { - ValueNode node = (ValueNode) duplicated; - if (node.stamp() == StampFactory.dependency()) { - // re-route dependencies to the merge - replaceUsagesOutside(node, newBottomMerge, duplicatedNodes); - // TODO(ls) maybe introduce phis for dependencies - } else if (node.stamp() == StampFactory.extension() || node.stamp() == StampFactory.condition()) { - cloneForOutsideUsages = true; - } else { - // introduce a new phi - PhiNode newPhi = bottomPhis.get(node); - if (newPhi == null) { - newPhi = graph.add(new PhiNode(node.kind(), newBottomMerge)); - bottomPhis.put(node, newPhi); - newPhi.addInput(node); - } - replaceUsagesOutside(node, newPhi, duplicatedNodes); - } - } else { - cloneForOutsideUsages = true; - } - if (cloneForOutsideUsages) { - // clone the offending node to the outside - Node newOutsideClone = duplicated.copyWithInputs(); - replaceUsagesOutside(duplicated, newOutsideClone, duplicatedNodes); - // this might cause other nodes to have outside usages, we need to look at those as well - for (Node input : newOutsideClone.inputs()) { - if (duplicatedNodes.contains(input)) { - worklist.add(input); - } - } - } - } - // check if this node has an input that lies outside and cannot be shared - for (Node input : duplicated.inputs()) { - if (!duplicatedNodes.contains(input)) { - boolean duplicateInput = false; - if (input instanceof VirtualState) { - duplicateInput = true; - } else if (input instanceof ValueNode) { - Stamp inputStamp = ((ValueNode) input).stamp(); - if (inputStamp == StampFactory.extension() || inputStamp == StampFactory.condition()) { - duplicateInput = true; - } - } - if (duplicateInput) { - duplicatedNodes.add(input); - worklist.add(input); - } - } - } - } - } - - /** - * Moves all depdendencies that point outside the duplicated area to the supplied value anchor node. - * - * @param duplicatedNodes The set of duplicated nodes. - * @param anchor The node that will be the new target for all dependencies that point outside the duplicated set of nodes. - */ - private static void retargetDependencies(HashSet duplicatedNodes, ValueAnchorNode anchor) { - for (Node node : duplicatedNodes) { - if (node instanceof ValueNode) { - NodeInputList dependencies = ((ValueNode) node).dependencies(); - for (int i = 0; i < dependencies.size(); i++) { - Node dependency = dependencies.get(i); - if (dependency != null && !duplicatedNodes.contains(dependency)) { - Debug.log("retargeting dependency %s to %s on %s", dependency, anchor, node); - dependencies.set(i, anchor); - } - } - } - } - } - - /** - * Checks if the given node has usages that are not within the given set of nodes. - * - * @param node The node whose usages are checked. - * @param nodeSet The set of nodes that are considered to be "within". - * @return true if the given node has usages on the outside, false otherwise. - */ - private static boolean hasUsagesOutside(Node node, HashSet nodeSet) { - for (Node usage : node.usages()) { - if (!nodeSet.contains(usage)) { - return true; - } - } - return false; - } - - /** - * Replaces the given node with the given replacement at all usages that are not within the given set of nodes. - * - * @param node The node to be replaced at outside usages. - * @param replacement The node that replaced the given node at outside usages. - * @param nodeSet The set of nodes that are considered to be "within". - */ - private static void replaceUsagesOutside(Node node, Node replacement, HashSet nodeSet) { - for (Node usage : node.usages().snapshot()) { - if (!nodeSet.contains(usage)) { - usage.replaceFirstInput(node, replacement); - } - } - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Sun Oct 07 14:27:50 2012 +0200 @@ -32,7 +32,6 @@ import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; public class SchedulePhase extends Phase { private ControlFlowGraph cfg; diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/InliningUtil.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/InliningUtil.java Sun Oct 07 14:15:44 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,960 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.phases.util; - -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.JavaType.Representation; -import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.FrameState.InliningIdentifier; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.phases.*; - -public class InliningUtil { - - private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); - - public interface InliningCallback { - StructuredGraph buildGraph(ResolvedJavaMethod method); - double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke); - void recordMethodContentsAssumption(ResolvedJavaMethod method); - void recordConcreteMethodAssumption(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl); - } - - public static String methodName(ResolvedJavaMethod method, Invoke invoke) { - if (!Debug.isLogEnabled()) { - return null; - } else if (invoke != null && invoke.stateAfter() != null) { - return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.codeSize() + " bytes)"; - } else { - return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.codeSize() + " bytes)"; - } - } - - public static String methodName(InlineInfo info) { - if (!Debug.isLogEnabled()) { - return null; - } else if (info.invoke != null && info.invoke.stateAfter() != null) { - return methodName(info.invoke.stateAfter(), info.invoke.bci()) + ": " + info.toString(); - } else { - return info.toString(); - } - } - - private static String methodName(FrameState frameState, int bci) { - StringBuilder sb = new StringBuilder(); - if (frameState.outerFrameState() != null) { - sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); - sb.append("->"); - } - sb.append(MetaUtil.format("%h.%n", frameState.method())); - sb.append("@").append(bci); - return sb.toString(); - } - - /** - * Represents an opportunity for inlining at the given invoke, with the given weight and level. - * The weight is the amortized weight of the additional code - so smaller is better. - * The level is the number of nested inlinings that lead to this invoke. - */ - public abstract static class InlineInfo implements Comparable { - public final Invoke invoke; - public final double weight; - public final int level; - - public InlineInfo(Invoke invoke, double weight, int level) { - this.invoke = invoke; - this.weight = weight; - this.level = level; - } - - public abstract int compiledCodeSize(); - - @Override - public int compareTo(InlineInfo o) { - return (weight < o.weight) ? -1 : (weight > o.weight) ? 1 : 0; - } - - protected static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) { - return Debug.scope("GetInliningGraph", concrete, new Callable() { - @Override - public StructuredGraph call() throws Exception { - return callback.buildGraph(concrete); - } - }); - } - - public abstract boolean canDeopt(); - - /** - * Performs the inlining described by this object and returns the node that represents the return value of the - * inlined method (or null for void methods and methods that have no non-exceptional exit). - * - * @param graph - * @param runtime - * @param callback - */ - public abstract void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback); - } - - /** - * Represents an inlining opportunity where the compiler can statically determine a monomorphic target method and - * therefore is able to determine the called method exactly. - */ - private static class ExactInlineInfo extends InlineInfo { - public final ResolvedJavaMethod concrete; - - public ExactInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaMethod concrete) { - super(invoke, weight, level); - this.concrete = concrete; - } - - @Override - public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, final InliningCallback callback) { - StructuredGraph graph = getGraph(concrete, callback); - assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); - callback.recordMethodContentsAssumption(concrete); - InliningUtil.inline(invoke, graph, true); - } - - @Override - public int compiledCodeSize() { - return concrete.compiledCodeSize(); - } - - @Override - public String toString() { - return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - @Override - public boolean canDeopt() { - return false; - } - } - - /** - * Represents an inlining opportunity for which profiling information suggests a monomorphic receiver, but for which - * the receiver type cannot be proven. A type check guard will be generated if this inlining is performed. - */ - private static class TypeGuardInlineInfo extends InlineInfo { - public final ResolvedJavaMethod concrete; - public final ResolvedJavaType type; - - public TypeGuardInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaMethod concrete, ResolvedJavaType type) { - super(invoke, weight, level); - this.concrete = concrete; - this.type = type; - } - - @Override - public int compiledCodeSize() { - return concrete.compiledCodeSize(); - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { - // receiver null check must be before the type check - InliningUtil.receiverNullCheck(invoke); - ValueNode receiver = invoke.methodCallTarget().receiver(); - LoadHubNode receiverHub = graph.add(new LoadHubNode(receiver)); - ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), runtime, graph); - ObjectEqualsNode typeCheck = graph.unique(new ObjectEqualsNode(receiverHub, typeHub)); - FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, invoke.leafGraphId())); - ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); - assert invoke.predecessor() != null; - - ValueNode anchoredReceiver = createAnchoredReceiver(graph, anchor, type, receiver, true); - invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - graph.addBeforeFixed(invoke.node(), receiverHub); - graph.addBeforeFixed(invoke.node(), guard); - graph.addBeforeFixed(invoke.node(), anchor); - - StructuredGraph calleeGraph = getGraph(concrete, callback); - assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); - callback.recordMethodContentsAssumption(concrete); - InliningUtil.inline(invoke, calleeGraph, false); - } - - @Override - public String toString() { - return "type-checked " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - @Override - public boolean canDeopt() { - return true; - } - } - - /** - * Polymorphic inlining of m methods with n type checks (n >= m) in case that the profiling information suggests a reasonable - * amounts of different receiver types and different methods. If an unknown type is encountered a deoptimization is triggered. - */ - private static class MultiTypeGuardInlineInfo extends InlineInfo { - public final List concretes; - public final ProfiledType[] ptypes; - public final int[] typesToConcretes; - public final double notRecordedTypeProbability; - - public MultiTypeGuardInlineInfo(Invoke invoke, double weight, int level, List concretes, ProfiledType[] ptypes, - int[] typesToConcretes, double notRecordedTypeProbability) { - super(invoke, weight, level); - assert concretes.size() > 0 && concretes.size() <= ptypes.length : "must have at least one method but no more than types methods"; - assert ptypes.length == typesToConcretes.length : "array lengths must match"; - - this.concretes = concretes; - this.ptypes = ptypes; - this.typesToConcretes = typesToConcretes; - this.notRecordedTypeProbability = notRecordedTypeProbability; - } - - @Override - public int compiledCodeSize() { - int result = 0; - for (ResolvedJavaMethod m: concretes) { - result += m.compiledCodeSize(); - } - return result; - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { - int numberOfMethods = concretes.size(); - boolean hasReturnValue = invoke.node().kind() != Kind.Void; - - // receiver null check must be the first node - InliningUtil.receiverNullCheck(invoke); - if (numberOfMethods > 1 || shouldFallbackToInvoke()) { - inlineMultipleMethods(graph, runtime, callback, numberOfMethods, hasReturnValue); - } else { - inlineSingleMethod(graph, runtime, callback); - } - } - - private boolean shouldFallbackToInvoke() { - return notRecordedTypeProbability > 0; - } - - private void inlineMultipleMethods(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, int numberOfMethods, boolean hasReturnValue) { - FixedNode continuation = invoke.next(); - - ValueNode originalReceiver = invoke.methodCallTarget().receiver(); - // setup merge and phi nodes for results and exceptions - MergeNode returnMerge = graph.add(new MergeNode()); - returnMerge.setProbability(invoke.probability()); - returnMerge.setStateAfter(invoke.stateAfter().duplicate(invoke.stateAfter().bci)); - - PhiNode returnValuePhi = null; - if (hasReturnValue) { - returnValuePhi = graph.unique(new PhiNode(invoke.node().kind(), returnMerge)); - } - - MergeNode exceptionMerge = null; - PhiNode exceptionObjectPhi = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - DispatchBeginNode exceptionEdge = invokeWithException.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - - exceptionMerge = graph.add(new MergeNode()); - exceptionMerge.setProbability(exceptionEdge.probability()); - - FixedNode exceptionSux = exceptionObject.next(); - graph.addBeforeFixed(exceptionSux, exceptionMerge); - exceptionObjectPhi = graph.unique(new PhiNode(Kind.Object, exceptionMerge)); - exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Void, exceptionObjectPhi)); - } - - // create one separate block for each invoked method - BeginNode[] calleeEntryNodes = new BeginNode[numberOfMethods]; - for (int i = 0; i < numberOfMethods; i++) { - int predecessors = 0; - double probability = 0; - for (int j = 0; j < typesToConcretes.length; j++) { - if (typesToConcretes[j] == i) { - predecessors++; - probability += ptypes[j].probability; - } - } - - calleeEntryNodes[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, predecessors, invoke.probability() * probability, true); - } - - // create the successor for an unknown type - FixedNode unknownTypeNode; - if (shouldFallbackToInvoke()) { - unknownTypeNode = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, 1, notRecordedTypeProbability, false); - } else { - unknownTypeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); - } - - // replace the invoke exception edge - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; - BeginNode exceptionEdge = invokeWithExceptionNode.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - exceptionObject.replaceAtUsages(exceptionObjectPhi); - exceptionObject.setNext(null); - GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); - } - - // replace the invoke with a switch on the type of the actual receiver - LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver())); - graph.addBeforeFixed(invoke.node(), receiverHub); - FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, calleeEntryNodes, unknownTypeNode); - - assert invoke.next() == continuation; - invoke.setNext(null); - returnMerge.setNext(continuation); - invoke.node().replaceAtUsages(returnValuePhi); - invoke.node().replaceAndDelete(dispatchOnType); - - ArrayList replacements = new ArrayList<>(); - - // do the actual inlining for every invoke - for (int i = 0; i < calleeEntryNodes.length; i++) { - BeginNode node = calleeEntryNodes[i]; - Invoke invokeForInlining = (Invoke) node.next(); - - ResolvedJavaType commonType = getLeastCommonType(i); - ValueNode receiver = invokeForInlining.methodCallTarget().receiver(); - boolean exact = getTypeCount(i) == 1; - PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); - invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - ResolvedJavaMethod concrete = concretes.get(i); - StructuredGraph calleeGraph = getGraph(concrete, callback); - callback.recordMethodContentsAssumption(concrete); - assert !IntrinsificationPhase.canIntrinsify(invokeForInlining, concrete, runtime); - InliningUtil.inline(invokeForInlining, calleeGraph, false); - replacements.add(anchoredReceiver); - } - if (shouldFallbackToInvoke()) { - replacements.add(null); - } - if (GraalOptions.OptTailDuplication) { - /* - * We might want to perform tail duplication at the merge after a type switch, if there are invokes that would - * benefit from the improvement in type information. - */ - FixedNode current = returnMerge; - int opportunities = 0; - do { - if (current instanceof InvokeNode && ((InvokeNode) current).methodCallTarget().receiver() == originalReceiver) { - opportunities++; - } else if (current.inputs().contains(originalReceiver)) { - opportunities++; - } - current = ((FixedWithNextNode) current).next(); - } while (current instanceof FixedWithNextNode); - if (opportunities > 0) { - metricInliningTailDuplication.increment(); - Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); - TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacements); - } - } - } - - private int getTypeCount(int concreteMethodIndex) { - int count = 0; - for (int i = 0; i < typesToConcretes.length; i++) { - if (typesToConcretes[i] == concreteMethodIndex) { - count++; - } - } - return count; - } - - private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { - ResolvedJavaType commonType = null; - for (int i = 0; i < typesToConcretes.length; i++) { - if (typesToConcretes[i] == concreteMethodIndex) { - if (commonType == null) { - commonType = ptypes[i].type; - } else { - commonType = commonType.leastCommonAncestor(ptypes[i].type); - } - } - } - assert commonType != null; - return commonType; - } - - private void inlineSingleMethod(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { - assert concretes.size() == 1 && ptypes.length > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; - - MergeNode calleeEntryNode = graph.add(new MergeNode()); - calleeEntryNode.setProbability(invoke.probability()); - LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver())); - graph.addBeforeFixed(invoke.node(), receiverHub); - - FixedNode unknownTypeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); - FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, new BeginNode[] {calleeEntryNode}, unknownTypeNode); - - FixedWithNextNode pred = (FixedWithNextNode) invoke.node().predecessor(); - pred.setNext(dispatchOnType); - calleeEntryNode.setNext(invoke.node()); - - ResolvedJavaMethod concrete = concretes.get(0); - StructuredGraph calleeGraph = getGraph(concrete, callback); - assert !IntrinsificationPhase.canIntrinsify(invoke, concrete, runtime); - callback.recordMethodContentsAssumption(concrete); - InliningUtil.inline(invoke, calleeGraph, false); - } - - private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode objectClassNode, BeginNode[] calleeEntryNodes, FixedNode unknownTypeSux) { - assert ptypes.length > 1; - - ResolvedJavaType[] types = new ResolvedJavaType[ptypes.length]; - double[] probabilities = new double[ptypes.length + 1]; - BeginNode[] successors = new BeginNode[ptypes.length + 1]; - int[] keySuccessors = new int[ptypes.length + 1]; - for (int i = 0; i < ptypes.length; i++) { - types[i] = ptypes[i].type; - probabilities[i] = ptypes[i].probability; - FixedNode entry = calleeEntryNodes[typesToConcretes[i]]; - if (entry instanceof MergeNode) { - EndNode endNode = graph.add(new EndNode()); - ((MergeNode) entry).addForwardEnd(endNode); - entry = endNode; - } - successors[i] = BeginNode.begin(entry); - keySuccessors[i] = i; - } - assert !(unknownTypeSux instanceof MergeNode); - successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); - probabilities[successors.length - 1] = notRecordedTypeProbability; - keySuccessors[successors.length - 1] = successors.length - 1; - - TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(objectClassNode, successors, probabilities, types, probabilities, keySuccessors)); - - return typeSwitch; - } - - private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, - MergeNode exceptionMerge, PhiNode exceptionObjectPhi, int predecessors, double probability, boolean useForInlining) { - Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining, probability); - BeginNode calleeEntryNode = graph.add(predecessors > 1 ? new MergeNode() : new BeginNode()); - calleeEntryNode.setNext(duplicatedInvoke.node()); - calleeEntryNode.setProbability(probability); - - EndNode endNode = graph.add(new EndNode()); - endNode.setProbability(probability); - - duplicatedInvoke.setNext(endNode); - returnMerge.addForwardEnd(endNode); - - if (returnValuePhi != null) { - returnValuePhi.addInput(duplicatedInvoke.node()); - } - return calleeEntryNode; - } - - private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining, double probability) { - Invoke result = (Invoke) invoke.node().copyWithInputs(); - Node callTarget = result.callTarget().copyWithInputs(); - result.node().replaceFirstInput(result.callTarget(), callTarget); - result.setUseForInlining(useForInlining); - result.setProbability(probability); - - Kind kind = invoke.node().kind(); - if (!kind.isVoid()) { - FrameState stateAfter = invoke.stateAfter(); - stateAfter = stateAfter.duplicate(stateAfter.bci); - stateAfter.replaceFirstInput(invoke.node(), result.node()); - result.setStateAfter(stateAfter); - } - - if (invoke instanceof InvokeWithExceptionNode) { - assert exceptionMerge != null && exceptionObjectPhi != null; - - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - BeginNode exceptionEdge = invokeWithException.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - FrameState stateAfterException = exceptionObject.stateAfter(); - - BeginNode newExceptionEdge = (BeginNode) exceptionEdge.copyWithInputs(); - ExceptionObjectNode newExceptionObject = (ExceptionObjectNode) exceptionObject.copyWithInputs(); - // set new state (pop old exception object, push new one) - newExceptionObject.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionObject)); - newExceptionEdge.setNext(newExceptionObject); - - EndNode endNode = graph.add(new EndNode()); - newExceptionObject.setNext(endNode); - exceptionMerge.addForwardEnd(endNode); - exceptionObjectPhi.addInput(newExceptionObject); - - ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); - } - return result; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); - builder.append(String.format(", %d methods with %d type checks:", concretes.size(), ptypes.length)); - for (int i = 0; i < concretes.size(); i++) { - builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); - } - return builder.toString(); - } - - @Override - public boolean canDeopt() { - return true; - } - } - - - /** - * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic target method, - * but for which an assumption has to be registered because of non-final classes. - */ - private static class AssumptionInlineInfo extends ExactInlineInfo { - public final ResolvedJavaType context; - - public AssumptionInlineInfo(Invoke invoke, double weight, int level, ResolvedJavaType context, ResolvedJavaMethod concrete) { - super(invoke, weight, level, concrete); - this.context = context; - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback) { - if (Debug.isLogEnabled()) { - String targetName = MetaUtil.format("%H.%n(%p):%r", invoke.methodCallTarget().targetMethod()); - String concreteName = MetaUtil.format("%H.%n(%p):%r", concrete); - Debug.log("recording concrete method assumption: %s on receiver type %s -> %s", targetName, context, concreteName); - } - callback.recordConcreteMethodAssumption(invoke.methodCallTarget().targetMethod(), context, concrete); - - super.inline(graph, runtime, callback); - } - - @Override - public String toString() { - return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - @Override - public boolean canDeopt() { - return true; - } - } - - /** - * Determines if inlining is possible at the given invoke node. - * @param invoke the invoke that should be inlined - * @param level the number of nested inlinings that lead to this invoke, or 0 if the invoke was part of the initial graph - * @param runtime a GraalRuntime instance used to determine of the invoke can be inlined and/or should be intrinsified - * @param callback a callback that is used to determine the weight of a specific inlining - * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke - */ - public static InlineInfo getInlineInfo(Invoke invoke, int level, GraalCodeCacheProvider runtime, Assumptions assumptions, InliningCallback callback, OptimisticOptimizations optimisticOpts) { - if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - // The invoke has already been lowered , or has been created as a low-level node. We have no method information. - return null; - } - ResolvedJavaMethod parent = invoke.stateAfter().method(); - MethodCallTargetNode callTarget = invoke.methodCallTarget(); - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - if (targetMethod == null) { - return null; - } - if (!checkInvokeConditions(invoke)) { - return null; - } - - if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { - if (checkTargetConditions(invoke, targetMethod, optimisticOpts)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, targetMethod, invoke); - return new ExactInlineInfo(invoke, weight, level, targetMethod); - } - return null; - } - ObjectStamp receiverStamp = callTarget.receiver().objectStamp(); - ResolvedJavaType receiverType = receiverStamp.type(); - if (receiverStamp.isExactType()) { - assert receiverType.isSubtypeOf(targetMethod.holder()) : receiverType + " subtype of " + targetMethod.holder() + " for " + targetMethod; - ResolvedJavaMethod resolved = receiverType.resolveMethodImpl(targetMethod); - if (checkTargetConditions(invoke, resolved, optimisticOpts)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, resolved, invoke); - return new ExactInlineInfo(invoke, weight, level, resolved); - } - return null; - } - ResolvedJavaType holder = targetMethod.holder(); - - if (receiverStamp.type() != null) { - // the invoke target might be more specific than the holder (happens after inlining: locals lose their declared type...) - // TODO (lstadler) fix this - if (receiverType != null && receiverType.isSubtypeOf(holder)) { - holder = receiverType; - } - } - // TODO (thomaswue) fix this - if (assumptions != null) { - ResolvedJavaMethod concrete = holder.uniqueConcreteMethod(targetMethod); - if (concrete != null) { - if (checkTargetConditions(invoke, concrete, optimisticOpts)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); - return new AssumptionInlineInfo(invoke, weight, level, holder, concrete); - } - return null; - } - } - - // type check based inlining - return getTypeCheckedInlineInfo(invoke, level, callback, parent, targetMethod, optimisticOpts); - } - - private static InlineInfo getTypeCheckedInlineInfo(Invoke invoke, int level, InliningCallback callback, ResolvedJavaMethod parent, ResolvedJavaMethod targetMethod, OptimisticOptimizations optimisticOpts) { - ProfilingInfo profilingInfo = parent.profilingInfo(); - JavaTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci()); - if (typeProfile != null) { - ProfiledType[] ptypes = typeProfile.getTypes(); - - if (ptypes != null && ptypes.length > 0) { - double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); - if (ptypes.length == 1 && notRecordedTypeProbability == 0) { - if (optimisticOpts.inlineMonomorphicCalls()) { - ResolvedJavaType type = ptypes[0].type; - ResolvedJavaMethod concrete = type.resolveMethodImpl(targetMethod); - if (checkTargetConditions(invoke, concrete, optimisticOpts)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); - return new TypeGuardInlineInfo(invoke, weight, level, concrete, type); - } - - Debug.log("not inlining %s because method can't be inlined", methodName(targetMethod, invoke)); - return null; - } else { - Debug.log("not inlining %s because GraalOptions.InlineMonomorphicCalls == false", methodName(targetMethod, invoke)); - return null; - } - } else { - invoke.setMegamorphic(true); - if (optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0 || optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { - // TODO (chaeubl) inlining of multiple methods should work differently - // 1. check which methods can be inlined - // 2. for those methods, use weight and probability to compute which of them should be inlined - // 3. do the inlining - // a) all seen methods can be inlined -> do so and guard with deopt - // b) some methods can be inlined -> inline them and fall back to invocation if violated - // TODO (chaeubl) sort types by probability - - // determine concrete methods and map type to specific method - ArrayList concreteMethods = new ArrayList<>(); - int[] typesToConcretes = new int[ptypes.length]; - for (int i = 0; i < ptypes.length; i++) { - ResolvedJavaMethod concrete = ptypes[i].type.resolveMethodImpl(targetMethod); - - int index = concreteMethods.indexOf(concrete); - if (index < 0) { - index = concreteMethods.size(); - concreteMethods.add(concrete); - } - typesToConcretes[i] = index; - } - - double totalWeight = 0; - boolean canInline = true; - for (ResolvedJavaMethod concrete: concreteMethods) { - if (!checkTargetConditions(invoke, concrete, optimisticOpts)) { - canInline = false; - break; - } - totalWeight += callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); - } - - if (canInline) { - return new MultiTypeGuardInlineInfo(invoke, totalWeight, level, concreteMethods, ptypes, typesToConcretes, notRecordedTypeProbability); - } else { - Debug.log("not inlining %s because it is a polymorphic method call and at least one invoked method cannot be inlined", methodName(targetMethod, invoke)); - return null; - } - } else { - if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { - Debug.log("not inlining %s because GraalOptions.InlinePolymorphicCalls == false", methodName(targetMethod, invoke)); - } else { - Debug.log("not inlining %s because GraalOptions.InlineMegamorphicCalls == false", methodName(targetMethod, invoke)); - } - return null; - } - } - } - - Debug.log("not inlining %s because no types/probabilities were recorded", methodName(targetMethod, invoke)); - return null; - } else { - Debug.log("not inlining %s because no type profile exists", methodName(targetMethod, invoke)); - return null; - } - } - - private static PiNode createAnchoredReceiver(StructuredGraph graph, FixedNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { - // to avoid that floating reads on receiver fields float above the type check - return graph.unique(new PiNode(receiver, anchor, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType))); - } - - private static boolean checkInvokeConditions(Invoke invoke) { - if (invoke.stateAfter() == null) { - Debug.log("not inlining %s because the invoke has no after state", methodName(invoke.methodCallTarget().targetMethod(), invoke)); - return false; - } - if (invoke.predecessor() == null) { - Debug.log("not inlining %s because the invoke is dead code", methodName(invoke.methodCallTarget().targetMethod(), invoke)); - return false; - } - if (!invoke.useForInlining()) { - Debug.log("not inlining %s because invoke is marked to be not used for inlining", methodName(invoke.methodCallTarget().targetMethod(), invoke)); - return false; - } - return true; - } - - private static boolean checkTargetConditions(Invoke invoke, JavaMethod method, OptimisticOptimizations optimisticOpts) { - if (method == null) { - Debug.log("not inlining because method is not resolved"); - return false; - } - if (!(method instanceof ResolvedJavaMethod)) { - Debug.log("not inlining %s because it is unresolved", method.toString()); - return false; - } - ResolvedJavaMethod resolvedMethod = (ResolvedJavaMethod) method; - if (Modifier.isNative(resolvedMethod.accessFlags())) { - Debug.log("not inlining %s because it is a native method", methodName(resolvedMethod, invoke)); - return false; - } - if (Modifier.isAbstract(resolvedMethod.accessFlags())) { - Debug.log("not inlining %s because it is an abstract method", methodName(resolvedMethod, invoke)); - return false; - } - if (!resolvedMethod.holder().isInitialized()) { - Debug.log("not inlining %s because of non-initialized class", methodName(resolvedMethod, invoke)); - return false; - } - if (!resolvedMethod.canBeInlined()) { - Debug.log("not inlining %s because it is marked non-inlinable", methodName(resolvedMethod, invoke)); - return false; - } - if (computeRecursiveInliningLevel(invoke.stateAfter(), (ResolvedJavaMethod) method) > GraalOptions.MaximumRecursiveInlining) { - Debug.log("not inlining %s because it exceeds the maximum recursive inlining depth", methodName(resolvedMethod, invoke)); - return false; - } - OptimisticOptimizations calleeOpts = new OptimisticOptimizations(resolvedMethod); - if (calleeOpts.lessOptimisticThan(optimisticOpts)) { - Debug.log("not inlining %s because callee uses less optimistic optimizations than caller", methodName(resolvedMethod, invoke)); - return false; - } - - return true; - } - - private static int computeRecursiveInliningLevel(FrameState state, ResolvedJavaMethod method) { - assert state != null; - - int count = 0; - FrameState curState = state; - while (curState != null) { - if (curState.method() == method) { - count++; - } - curState = curState.outerFrameState(); - } - return count; - } - - /** - * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. - * - * @param invoke the invoke that will be replaced - * @param inlineGraph the graph that the invoke will be replaced with - * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required - */ - public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { - InliningIdentifier identifier = new InliningIdentifier(inlineGraph.method(), invoke); - NodeInputList parameters = invoke.callTarget().arguments(); - StructuredGraph graph = (StructuredGraph) invoke.node().graph(); - - FrameState stateAfter = invoke.stateAfter(); - assert stateAfter.isAlive(); - - IdentityHashMap replacements = new IdentityHashMap<>(); - ArrayList nodes = new ArrayList<>(); - ReturnNode returnNode = null; - UnwindNode unwindNode = null; - StartNode entryPointNode = inlineGraph.start(); - FixedNode firstCFGNode = entryPointNode.next(); - for (Node node : inlineGraph.getNodes()) { - if (node == entryPointNode || node == entryPointNode.stateAfter()) { - // Do nothing. - } else if (node instanceof LocalNode) { - replacements.put(node, parameters.get(((LocalNode) node).index())); - } else { - nodes.add(node); - if (node instanceof ReturnNode) { - assert returnNode == null; - returnNode = (ReturnNode) node; - } else if (node instanceof UnwindNode) { - assert unwindNode == null; - unwindNode = (UnwindNode) node; - } - } - } - replacements.put(entryPointNode, BeginNode.prevBegin(invoke.node())); // ensure proper anchoring of things that where anchored to the StartNode - - assert invoke.node().successors().first() != null : invoke; - assert invoke.node().predecessor() != null; - - Map duplicates = graph.addDuplicates(nodes, replacements); - FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); - if (receiverNullCheck) { - receiverNullCheck(invoke); - } - invoke.node().replaceAtPredecessor(firstCFGNodeDuplicate); - - FrameState stateAtExceptionEdge = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); - if (unwindNode != null) { - assert unwindNode.predecessor() != null; - assert invokeWithException.exceptionEdge().successors().count() == 1; - ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge().next(); - stateAtExceptionEdge = obj.stateAfter(); - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - obj.replaceAtUsages(unwindDuplicate.exception()); - unwindDuplicate.clearInputs(); - Node n = obj.next(); - obj.setNext(null); - unwindDuplicate.replaceAndDelete(n); - } else { - invokeWithException.killExceptionEdge(); - } - } else { - if (unwindNode != null) { - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - DeoptimizeNode deoptimizeNode = new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler, invoke.leafGraphId()); - unwindDuplicate.replaceAndDelete(graph.add(deoptimizeNode)); - // move the deopt upwards if there is a monitor exit that tries to use the "after exception" frame state - // (because there is no "after exception" frame state!) - if (deoptimizeNode.predecessor() instanceof MonitorExitNode) { - MonitorExitNode monitorExit = (MonitorExitNode) deoptimizeNode.predecessor(); - if (monitorExit.stateAfter() != null && monitorExit.stateAfter().bci == FrameState.AFTER_EXCEPTION_BCI) { - FrameState monitorFrameState = monitorExit.stateAfter(); - graph.removeFixed(monitorExit); - monitorFrameState.safeDelete(); - } - } - } - } - - FrameState outerFrameState = null; - double invokeProbability = invoke.node().probability(); - for (Node node : duplicates.values()) { - if (GraalOptions.ProbabilityAnalysis) { - if (node instanceof FixedNode) { - FixedNode fixed = (FixedNode) node; - double newProbability = fixed.probability() * invokeProbability; - if (GraalOptions.LimitInlinedProbability) { - newProbability = Math.min(newProbability, invokeProbability); - } - fixed.setProbability(newProbability); - } - } - if (node instanceof FrameState) { - FrameState frameState = (FrameState) node; - assert frameState.bci != FrameState.BEFORE_BCI; - if (frameState.bci == FrameState.AFTER_BCI) { - frameState.replaceAndDelete(stateAfter); - } else if (frameState.bci == FrameState.AFTER_EXCEPTION_BCI) { - if (frameState.isAlive()) { - assert stateAtExceptionEdge != null; - frameState.replaceAndDelete(stateAtExceptionEdge); - } else { - assert stateAtExceptionEdge == null; - } - } else { - // only handle the outermost frame states - if (frameState.outerFrameState() == null) { - assert frameState.method() == inlineGraph.method(); - if (outerFrameState == null) { - outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); - outerFrameState.setDuringCall(true); - } - frameState.setOuterFrameState(outerFrameState); - frameState.setInliningIdentifier(identifier); - } - } - } - } - - Node returnValue = null; - if (returnNode != null) { - if (returnNode.result() instanceof LocalNode) { - returnValue = replacements.get(returnNode.result()); - } else { - returnValue = duplicates.get(returnNode.result()); - } - invoke.node().replaceAtUsages(returnValue); - Node returnDuplicate = duplicates.get(returnNode); - returnDuplicate.clearInputs(); - Node n = invoke.next(); - invoke.setNext(null); - returnDuplicate.replaceAndDelete(n); - } - - invoke.node().clearInputs(); - invoke.node().replaceAtUsages(null); - GraphUtil.killCFG(invoke.node()); - - if (stateAfter.usages().isEmpty()) { - stateAfter.safeDelete(); - } - } - - public static void receiverNullCheck(Invoke invoke) { - MethodCallTargetNode callTarget = invoke.methodCallTarget(); - StructuredGraph graph = (StructuredGraph) invoke.graph(); - NodeInputList parameters = callTarget.arguments(); - ValueNode firstParam = parameters.size() <= 0 ? null : parameters.get(0); - if (!callTarget.isStatic() && firstParam.kind() == Kind.Object && !firstParam.objectStamp().nonNull()) { - graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new IsNullNode(firstParam)), DeoptimizationReason.ClassCastException, DeoptimizationAction.InvalidateReprofile, true, invoke.leafGraphId()))); - } - } -} diff -r 2c913b643422 -r ee651c726397 graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinterObserver.java --- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinterObserver.java Sun Oct 07 14:15:44 2012 +0200 +++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinterObserver.java Sun Oct 07 14:27:50 2012 +0200 @@ -37,7 +37,6 @@ import com.oracle.graal.lir.*; import com.oracle.graal.lir.cfg.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.*; /** * Observes compilation events and uses {@link CFGPrinter} to produce a control flow graph for the