8178088: Update Graal
authoriveresov
Thu, 06 Apr 2017 14:31:32 -0700
changeset 46371 0337d0617e7b
parent 46366 b9a1aa504eb5
child 46372 721b8f969cc8
8178088: Update Graal Summary: Update Graal, do appropriate changes to jaotc Reviewed-by: kvn
hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTBackend.java
hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTCompilationTask.java
hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTHotSpotResolvedJavaMethod.java
hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTStub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ProbabilityDirectiveTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.sparc/src/org/graalvm/compiler/asm/sparc/SPARCAssembler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceStatisticsPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/Condition.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCLIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/BoxingEliminationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest3.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationMulTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest1.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest11.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest13.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest5.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DeMorganCanonicalizationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FindUniqueDefaultMethodTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LockEliminationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NestedLoopTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PhiCreationTests.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SimpleCFGTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyDebugUsageTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/GraalTutorial.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugRetryableTask.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GraalDebugConfig.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeBitMap.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeStack.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicate.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicates.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ClassSubstitutionsTests.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorldTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/JVMCIInfopointErrorTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/LoadJavaMirrorWithKlassTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotCompiledCodeBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalJVMCIServiceLocator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntimeProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalVMEventListener.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/PrintStreamOptionKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/GraalHotSpotVMConfigNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/StubForeignCallNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/EncodedSymbolNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/LoadJavaMirrorWithKlassPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/EliminateRedundantInitializationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/LoadExceptionObjectSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ReflectionGetCallerClassNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/Stub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCControlFlow.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/SwitchStrategy.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanWalker.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessAnalysisPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceBuilderPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTreeAnalyzer.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ssa/SSAVerifier.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/LSStackSlotAllocator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopFullUnrollPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopUnswitchingPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopsDataTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/micro/benchmarks/ArrayListBenchmark.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/trace/TraceLSRAIntervalBuildingBench.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FixedGuardNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiArrayNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/SimplifyingGraphDecoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeCopyNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/NodeIntrinsicPluginFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorIdNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/NodeValueMap.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/ModifiableOptionValues.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/UniquePathUtilities.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AbstractInliningPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DominatorConditionalEliminationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LockEliminationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NewConditionalEliminationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/MergeableState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/InstanceOfTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/GeneratedNodeIntrinsicPlugin.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/InjectedDependencies.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/NodeIntrinsicVerifier.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/NodeIntrinsificationProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider.processor/src/org/graalvm/compiler/serviceprovider/processor/ServiceProviderProcessor.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/EconomicMap.java
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTBackend.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTBackend.java	Thu Apr 06 14:31:32 2017 -0700
@@ -54,12 +54,9 @@
 import jdk.vm.ci.meta.TriState;
 
 public class AOTBackend {
-
     private final Main main;
     private final OptionValues graalOptions;
-
     private final HotSpotBackend backend;
-
     private final HotSpotProviders providers;
     private final HotSpotCodeCacheProvider codeCache;
     private final PhaseSuite<HighTierContext> graphBuilderSuite;
@@ -81,6 +78,10 @@
         return graphBuilderSuite;
     }
 
+    public HotSpotBackend getBackend() {
+        return backend;
+    }
+
     private Suites getSuites() {
         // create suites every time, as we modify options for the compiler
         return backend.getSuites().getDefaultSuites(graalOptions);
@@ -189,7 +190,7 @@
 
     public void printCompiledMethod(HotSpotResolvedJavaMethod resolvedMethod, CompilationResult compResult) {
         // This is really not installing the method.
-        InstalledCode installedCode = codeCache.addCode(resolvedMethod, HotSpotCompiledCodeBuilder.createCompiledCode(null, null, compResult), null, null);
+        InstalledCode installedCode = codeCache.addCode(resolvedMethod, HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, null, null, compResult), null, null);
         String disassembly = codeCache.disassemble(installedCode);
         if (disassembly != null) {
             main.printlnDebug(disassembly);
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTCompilationTask.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTCompilationTask.java	Thu Apr 06 14:31:32 2017 -0700
@@ -145,7 +145,7 @@
             aotBackend.printCompiledMethod((HotSpotResolvedJavaMethod) method, compResult);
         }
 
-        result = new CompiledMethodInfo(compResult, new AOTHotSpotResolvedJavaMethod((HotSpotResolvedJavaMethod) method));
+        result = new CompiledMethodInfo(compResult, new AOTHotSpotResolvedJavaMethod((HotSpotResolvedJavaMethod) method, aotBackend.getBackend()));
     }
 
     private String getMethodDescription() {
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTHotSpotResolvedJavaMethod.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTHotSpotResolvedJavaMethod.java	Thu Apr 06 14:31:32 2017 -0700
@@ -24,6 +24,7 @@
 package jdk.tools.jaotc;
 
 import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.core.target.Backend;
 import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
 import jdk.vm.ci.hotspot.HotSpotCompiledCode;
 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
@@ -31,9 +32,11 @@
 public class AOTHotSpotResolvedJavaMethod implements JavaMethodInfo {
 
     private final HotSpotResolvedJavaMethod method;
+    private final Backend backend;
 
-    public AOTHotSpotResolvedJavaMethod(HotSpotResolvedJavaMethod method) {
+    public AOTHotSpotResolvedJavaMethod(HotSpotResolvedJavaMethod method, Backend backend) {
         this.method = method;
+        this.backend = backend;
     }
 
     public String getSymbolName() {
@@ -46,7 +49,7 @@
     }
 
     public HotSpotCompiledCode compiledCode(CompilationResult result) {
-        return HotSpotCompiledCodeBuilder.createCompiledCode(method, null, result);
+        return HotSpotCompiledCodeBuilder.createCompiledCode(backend.getCodeCache(), method, null, result);
     }
 
 }
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTStub.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTStub.java	Thu Apr 06 14:31:32 2017 -0700
@@ -49,7 +49,7 @@
     }
 
     public HotSpotCompiledCode compiledCode(CompilationResult result) {
-        return HotSpotCompiledCodeBuilder.createCompiledCode(null, null, result);
+        return HotSpotCompiledCodeBuilder.createCompiledCode(backend.getCodeCache(), null, null, result);
     }
 
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ProbabilityDirectiveTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ProbabilityDirectiveTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,15 +22,16 @@
  */
 package org.graalvm.compiler.api.directives.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode;
+import org.junit.Assert;
+import org.junit.Test;
 
 public class ProbabilityDirectiveTest extends GraalCompilerTest {
 
@@ -55,9 +56,21 @@
         Assert.assertEquals("IfNode count", 1, ifNodes.count());
 
         IfNode ifNode = ifNodes.first();
-        AbstractBeginNode trueSuccessor = ifNode.trueSuccessor();
-        Assert.assertEquals("branch probability of " + ifNode, 0.125, ifNode.probability(trueSuccessor), 0);
+        AbstractBeginNode oneSuccessor;
+        if (returnValue(ifNode.trueSuccessor()) == 1) {
+            oneSuccessor = ifNode.trueSuccessor();
+        } else {
+            assert returnValue(ifNode.falseSuccessor()) == 1;
+            oneSuccessor = ifNode.falseSuccessor();
+        }
+        Assert.assertEquals("branch probability of " + ifNode, 0.125, ifNode.probability(oneSuccessor), 0);
 
         return true;
     }
+
+    private static int returnValue(AbstractBeginNode b) {
+        ControlFlowAnchorNode anchor = (ControlFlowAnchorNode) b.next();
+        ReturnNode returnNode = (ReturnNode) anchor.next();
+        return returnNode.result().asJavaConstant().asInt();
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.sparc/src/org/graalvm/compiler/asm/sparc/SPARCAssembler.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.sparc/src/org/graalvm/compiler/asm/sparc/SPARCAssembler.java	Thu Apr 06 14:31:32 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, 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
@@ -1757,6 +1757,10 @@
         return constant.isNull() || isSimm(constant.asLong(), 5);
     }
 
+    public static boolean isSimm5(long imm) {
+        return isSimm(imm, 5);
+    }
+
     public static boolean isSimm13(int imm) {
         return isSimm(imm, 13);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceStatisticsPrinter.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceStatisticsPrinter.java	Thu Apr 06 14:31:32 2017 -0700
@@ -35,7 +35,7 @@
     @SuppressWarnings("try")
     public static void printTraceStatistics(TraceBuilderResult result, String compilationUnitName) {
         try (Scope s = Debug.scope("DumpTraceStatistics")) {
-            if (Debug.isLogEnabled(Debug.VERBOSE_LOG_LEVEL)) {
+            if (Debug.isLogEnabled(Debug.VERBOSE_LEVEL)) {
                 print(result, compilationUnitName);
             }
         } catch (Throwable e) {
@@ -48,9 +48,9 @@
         List<Trace> traces = result.getTraces();
         int numTraces = traces.size();
 
-        try (Indent indent0 = Debug.logAndIndent(Debug.VERBOSE_LOG_LEVEL, "<tracestatistics>")) {
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "<name>%s</name>", compilationUnitName != null ? compilationUnitName : "null");
-            try (Indent indent1 = Debug.logAndIndent(Debug.VERBOSE_LOG_LEVEL, "<traces>")) {
+        try (Indent indent0 = Debug.logAndIndent(Debug.VERBOSE_LEVEL, "<tracestatistics>")) {
+            Debug.log(Debug.VERBOSE_LEVEL, "<name>%s</name>", compilationUnitName != null ? compilationUnitName : "null");
+            try (Indent indent1 = Debug.logAndIndent(Debug.VERBOSE_LEVEL, "<traces>")) {
                 printRawLine("tracenumber", "total", "min", "max", "numBlocks");
                 for (int i = 0; i < numTraces; i++) {
                     AbstractBlockBase<?>[] t = traces.get(i).getBlocks();
@@ -70,14 +70,14 @@
                     printLine(i, total, min, max, t.length);
                 }
             }
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "</traces>");
+            Debug.log(Debug.VERBOSE_LEVEL, "</traces>");
         }
-        Debug.log(Debug.VERBOSE_LOG_LEVEL, "</tracestatistics>");
+        Debug.log(Debug.VERBOSE_LEVEL, "</tracestatistics>");
 
     }
 
     private static void printRawLine(Object tracenr, Object totalTime, Object minProb, Object maxProb, Object numBlocks) {
-        Debug.log(Debug.VERBOSE_LOG_LEVEL, "%s", String.join(SEP, tracenr.toString(), totalTime.toString(), minProb.toString(), maxProb.toString(), numBlocks.toString()));
+        Debug.log(Debug.VERBOSE_LEVEL, "%s", String.join(SEP, tracenr.toString(), totalTime.toString(), minProb.toString(), maxProb.toString(), numBlocks.toString()));
     }
 
     private static void printLine(int tracenr, double totalTime, double minProb, double maxProb, int numBlocks) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/Condition.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/Condition.java	Thu Apr 06 14:31:32 2017 -0700
@@ -343,116 +343,7 @@
         if (lt instanceof PrimitiveConstant) {
             PrimitiveConstant lp = (PrimitiveConstant) lt;
             PrimitiveConstant rp = (PrimitiveConstant) rt;
-            switch (lp.getJavaKind()) {
-                case Boolean:
-                case Byte:
-                case Char:
-                case Short:
-                case Int: {
-                    int x = lp.asInt();
-                    int y = rp.asInt();
-                    switch (this) {
-                        case EQ:
-                            return x == y;
-                        case NE:
-                            return x != y;
-                        case LT:
-                            return x < y;
-                        case LE:
-                            return x <= y;
-                        case GT:
-                            return x > y;
-                        case GE:
-                            return x >= y;
-                        case AE:
-                            return UnsignedMath.aboveOrEqual(x, y);
-                        case BE:
-                            return UnsignedMath.belowOrEqual(x, y);
-                        case AT:
-                            return UnsignedMath.aboveThan(x, y);
-                        case BT:
-                            return UnsignedMath.belowThan(x, y);
-                        default:
-                            throw new GraalError("expected condition: %s", this);
-                    }
-                }
-                case Long: {
-                    long x = lp.asLong();
-                    long y = rp.asLong();
-                    switch (this) {
-                        case EQ:
-                            return x == y;
-                        case NE:
-                            return x != y;
-                        case LT:
-                            return x < y;
-                        case LE:
-                            return x <= y;
-                        case GT:
-                            return x > y;
-                        case GE:
-                            return x >= y;
-                        case AE:
-                            return UnsignedMath.aboveOrEqual(x, y);
-                        case BE:
-                            return UnsignedMath.belowOrEqual(x, y);
-                        case AT:
-                            return UnsignedMath.aboveThan(x, y);
-                        case BT:
-                            return UnsignedMath.belowThan(x, y);
-                        default:
-                            throw new GraalError("expected condition: %s", this);
-                    }
-                }
-                case Float: {
-                    float x = lp.asFloat();
-                    float y = rp.asFloat();
-                    if (Float.isNaN(x) || Float.isNaN(y)) {
-                        return unorderedIsTrue;
-                    }
-                    switch (this) {
-                        case EQ:
-                            return x == y;
-                        case NE:
-                            return x != y;
-                        case LT:
-                            return x < y;
-                        case LE:
-                            return x <= y;
-                        case GT:
-                            return x > y;
-                        case GE:
-                            return x >= y;
-                        default:
-                            throw new GraalError("expected condition: %s", this);
-                    }
-                }
-                case Double: {
-                    double x = lp.asDouble();
-                    double y = rp.asDouble();
-                    if (Double.isNaN(x) || Double.isNaN(y)) {
-                        return unorderedIsTrue;
-                    }
-                    switch (this) {
-                        case EQ:
-                            return x == y;
-                        case NE:
-                            return x != y;
-                        case LT:
-                            return x < y;
-                        case LE:
-                            return x <= y;
-                        case GT:
-                            return x > y;
-                        case GE:
-                            return x >= y;
-                        default:
-                            throw new GraalError("expected condition: %s", this);
-                    }
-                }
-                default:
-                    throw new GraalError("expected value kind %s while folding condition: %s", lp.getJavaKind(), this);
-            }
+            return foldCondition(lp, rp, unorderedIsTrue);
         } else {
             Boolean equal = constantReflection.constantEquals(lt, rt);
             if (equal == null) {
@@ -469,6 +360,128 @@
         }
     }
 
+    /**
+     * Attempts to fold a comparison between two primitive constants and return the result.
+     *
+     * @param lp the constant on the left side of the comparison
+     * @param rp the constant on the right side of the comparison
+     * @param unorderedIsTrue true if an undecided float comparison should result in "true"
+     * @return true if the comparison is known to be true, false if the comparison is known to be
+     *         false
+     */
+    public boolean foldCondition(PrimitiveConstant lp, PrimitiveConstant rp, boolean unorderedIsTrue) {
+        switch (lp.getJavaKind()) {
+            case Boolean:
+            case Byte:
+            case Char:
+            case Short:
+            case Int: {
+                int x = lp.asInt();
+                int y = rp.asInt();
+                switch (this) {
+                    case EQ:
+                        return x == y;
+                    case NE:
+                        return x != y;
+                    case LT:
+                        return x < y;
+                    case LE:
+                        return x <= y;
+                    case GT:
+                        return x > y;
+                    case GE:
+                        return x >= y;
+                    case AE:
+                        return UnsignedMath.aboveOrEqual(x, y);
+                    case BE:
+                        return UnsignedMath.belowOrEqual(x, y);
+                    case AT:
+                        return UnsignedMath.aboveThan(x, y);
+                    case BT:
+                        return UnsignedMath.belowThan(x, y);
+                    default:
+                        throw new GraalError("expected condition: %s", this);
+                }
+            }
+            case Long: {
+                long x = lp.asLong();
+                long y = rp.asLong();
+                switch (this) {
+                    case EQ:
+                        return x == y;
+                    case NE:
+                        return x != y;
+                    case LT:
+                        return x < y;
+                    case LE:
+                        return x <= y;
+                    case GT:
+                        return x > y;
+                    case GE:
+                        return x >= y;
+                    case AE:
+                        return UnsignedMath.aboveOrEqual(x, y);
+                    case BE:
+                        return UnsignedMath.belowOrEqual(x, y);
+                    case AT:
+                        return UnsignedMath.aboveThan(x, y);
+                    case BT:
+                        return UnsignedMath.belowThan(x, y);
+                    default:
+                        throw new GraalError("expected condition: %s", this);
+                }
+            }
+            case Float: {
+                float x = lp.asFloat();
+                float y = rp.asFloat();
+                if (Float.isNaN(x) || Float.isNaN(y)) {
+                    return unorderedIsTrue;
+                }
+                switch (this) {
+                    case EQ:
+                        return x == y;
+                    case NE:
+                        return x != y;
+                    case LT:
+                        return x < y;
+                    case LE:
+                        return x <= y;
+                    case GT:
+                        return x > y;
+                    case GE:
+                        return x >= y;
+                    default:
+                        throw new GraalError("expected condition: %s", this);
+                }
+            }
+            case Double: {
+                double x = lp.asDouble();
+                double y = rp.asDouble();
+                if (Double.isNaN(x) || Double.isNaN(y)) {
+                    return unorderedIsTrue;
+                }
+                switch (this) {
+                    case EQ:
+                        return x == y;
+                    case NE:
+                        return x != y;
+                    case LT:
+                        return x < y;
+                    case LE:
+                        return x <= y;
+                    case GT:
+                        return x > y;
+                    case GE:
+                        return x >= y;
+                    default:
+                        throw new GraalError("expected condition: %s", this);
+                }
+            }
+            default:
+                throw new GraalError("expected value kind %s while folding condition: %s", lp.getJavaKind(), this);
+        }
+    }
+
     public Condition join(Condition other) {
         if (other == this) {
             return this;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java	Thu Apr 06 14:31:32 2017 -0700
@@ -154,6 +154,7 @@
         return Double.isNaN(lowerBound);
     }
 
+    @Override
     public boolean isUnrestricted() {
         return lowerBound == Double.NEGATIVE_INFINITY && upperBound == Double.POSITIVE_INFINITY && !nonNaN;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java	Thu Apr 06 14:31:32 2017 -0700
@@ -59,6 +59,11 @@
     }
 
     @Override
+    public boolean isUnrestricted() {
+        return true;
+    }
+
+    @Override
     public Stamp empty() {
         return this;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java	Thu Apr 06 14:31:32 2017 -0700
@@ -254,6 +254,7 @@
         return upMask;
     }
 
+    @Override
     public boolean isUnrestricted() {
         return lowerBound == CodeUtil.minValue(getBits()) && upperBound == CodeUtil.maxValue(getBits()) && downMask == 0 && upMask == CodeUtil.mask(getBits());
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java	Thu Apr 06 14:31:32 2017 -0700
@@ -126,6 +126,13 @@
     }
 
     /**
+     * Tests whether this stamp represents all values of this kind.
+     */
+    public boolean isUnrestricted() {
+        return this.equals(this.unrestricted());
+    }
+
+    /**
      * If this stamp represents a single value, the methods returns this single value. It returns
      * null otherwise.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java	Thu Apr 06 14:31:32 2017 -0700
@@ -39,33 +39,6 @@
 
 public class StampFactory {
 
-    /*
-     * The marker stamp for node intrinsics must be its own class, so that it is never equal() to a
-     * regular ObjectStamp.
-     */
-    static final class NodeIntrinsicStamp extends ObjectStamp {
-        protected static final Stamp SINGLETON = new NodeIntrinsicStamp();
-
-        private NodeIntrinsicStamp() {
-            super(null, false, false, false);
-        }
-
-        @Override
-        public int hashCode() {
-            return System.identityHashCode(this);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return this == obj;
-        }
-
-        @Override
-        public String toString() {
-            return "NodeIntrinsicStamp";
-        }
-    }
-
     // JaCoCo Exclude
 
     private static final Stamp[] stampCache = new Stamp[JavaKind.values().length];
@@ -143,14 +116,6 @@
         return VoidStamp.getInstance();
     }
 
-    /**
-     * A stamp used only in the graph of intrinsics, e.g., snippets. It is then replaced by an
-     * actual stamp when the intrinsic is used, i.e., when the snippet template is instantiated.
-     */
-    public static Stamp forNodeIntrinsic() {
-        return NodeIntrinsicStamp.SINGLETON;
-    }
-
     public static Stamp intValue() {
         return forKind(JavaKind.Int);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java	Thu Apr 06 14:31:32 2017 -0700
@@ -46,6 +46,11 @@
     }
 
     @Override
+    public boolean isUnrestricted() {
+        return true;
+    }
+
+    @Override
     public JavaKind getStackKind() {
         return JavaKind.Void;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -23,10 +23,10 @@
 package org.graalvm.compiler.core.common.util;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.Map;
+
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.Equivalence;
 
 /**
  * Creates an array of T objects order by the occurrence frequency of each object. The most
@@ -49,24 +49,24 @@
         }
     }
 
-    protected final Map<T, Entry<T>> map;
+    protected final EconomicMap<T, Entry<T>> map;
     protected boolean containsNull;
 
     /**
      * Creates an encoder that uses object identity.
      */
     public static <T> FrequencyEncoder<T> createIdentityEncoder() {
-        return new FrequencyEncoder<>(new IdentityHashMap<>());
+        return new FrequencyEncoder<>(EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE));
     }
 
     /**
      * Creates an encoder that uses {@link Object#equals(Object) object equality}.
      */
     public static <T> FrequencyEncoder<T> createEqualityEncoder() {
-        return new FrequencyEncoder<>(new HashMap<>());
+        return new FrequencyEncoder<>(EconomicMap.create(Equivalence.DEFAULT));
     }
 
-    protected FrequencyEncoder(Map<T, Entry<T>> map) {
+    protected FrequencyEncoder(EconomicMap<T, Entry<T>> map) {
         this.map = map;
     }
 
@@ -91,7 +91,7 @@
      * Returns the index of an object in the array. The object must have been
      * {@link #addObject(Object) added} before.
      */
-    public int getIndex(Object object) {
+    public int getIndex(T object) {
         if (object == null) {
             assert containsNull;
             return 0;
@@ -114,7 +114,10 @@
      */
     public T[] encodeAll(T[] allObjects) {
         assert allObjects.length == getLength();
-        List<Entry<T>> sortedEntries = new ArrayList<>(map.values());
+        List<Entry<T>> sortedEntries = new ArrayList<>(allObjects.length);
+        for (Entry<T> value : map.getValues()) {
+            sortedEntries.add(value);
+        }
         sortedEntries.sort((e1, e2) -> -Integer.compare(e1.frequency, e2.frequency));
 
         int offset = 0;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java	Thu Apr 06 14:31:32 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, 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
@@ -202,7 +202,7 @@
 
     private Variable emitUnary(Op3s op3, Value input) {
         Variable result = getLIRGen().newVariable(LIRKind.combine(input));
-        getLIRGen().append(SPARCOP3Op.newUnary(op3, input, result));
+        getLIRGen().append(SPARCOP3Op.newUnary(op3, getLIRGen().loadSimm13(input), result));
         return result;
     }
 
@@ -227,9 +227,9 @@
     private Variable emitBinary(ValueKind<?> resultKind, Op3s op3, Value a, Value b, LIRFrameState state) {
         Variable result = getLIRGen().newVariable(resultKind);
         if (op3.isCommutative() && isJavaConstant(a) && getLIRGen().getMoveFactory().canInlineConstant(asJavaConstant(a))) {
-            getLIRGen().append(new SPARCOP3Op(op3, getLIRGen().load(b), a, result, state));
+            getLIRGen().append(new SPARCOP3Op(op3, getLIRGen().load(b), getLIRGen().loadSimm13(a), result, state));
         } else {
-            getLIRGen().append(new SPARCOP3Op(op3, getLIRGen().load(a), b, result, state));
+            getLIRGen().append(new SPARCOP3Op(op3, getLIRGen().load(a), getLIRGen().loadSimm13(b), result, state));
         }
         return result;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCLIRGenerator.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCLIRGenerator.java	Thu Apr 06 14:31:32 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, 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
@@ -199,11 +199,11 @@
         Condition actualCondition;
         if (isJavaConstant(x)) {
             left = load(y);
-            right = loadNonConst(x);
+            right = loadSimm13(x);
             actualCondition = cond.mirror();
         } else {
             left = load(x);
-            right = loadNonConst(y);
+            right = loadSimm13(y);
             actualCondition = cond;
         }
         SPARCKind actualCmpKind = (SPARCKind) cmpKind;
@@ -250,7 +250,7 @@
         return load(value);
     }
 
-    private Value loadSimm13(Value value) {
+    public Value loadSimm13(Value value) {
         if (isJavaConstant(value)) {
             JavaConstant c = asJavaConstant(value);
             if (c.isNull() || SPARCAssembler.isSimm13(c)) {
@@ -261,6 +261,13 @@
     }
 
     @Override
+    public Value loadNonConst(Value value) {
+        // SPARC does not support a proper way of loadNonConst. Please use the appropriate
+        // loadSimm11 or loadSimm13 variants.
+        throw GraalError.shouldNotReachHere("This operation is not available for SPARC.");
+    }
+
+    @Override
     public Variable emitConditionalMove(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue) {
         // Emit compare
         SPARCKind cmpSPARCKind = (SPARCKind) cmpKind;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/BoxingEliminationTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/BoxingEliminationTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,10 +22,6 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
 import org.graalvm.compiler.loop.DefaultLoopPolicies;
 import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
 import org.graalvm.compiler.nodes.ReturnNode;
@@ -37,6 +33,9 @@
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
 
 /**
  * In the following tests, the usages of local variable "a" are replaced with the integer constant
@@ -162,7 +161,7 @@
             sum0 = a;
         } else {
             int sum = a;
-            for (int i = 0; i < n; i++) {
+            for (int i = 1; i < n; i++) {
                 sum += i;
             }
             sum0 = sum;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest3.java	Thu Apr 06 14:31:32 2017 -0700
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2012, 2014, 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 org.graalvm.compiler.core.test;
+
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
+import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+public class CompareCanonicalizerTest3 extends GraalCompilerTest {
+
+    @SuppressWarnings("unused") private static int sink0;
+    @SuppressWarnings("unused") private static int sink1;
+
+    @Test
+    public void test00() {
+        assertCanonicallyEqual("integerTestCanonicalization00", "referenceSnippet00");
+    }
+
+    public static void integerTestCanonicalization00(char a) {
+        if (a - 1 < a) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public static void referenceSnippet00(char a) {
+        sink1 = 0;
+    }
+
+    @Ignore("Needs better stamp support for unsigned ranges")
+    @Test
+    public void test01() {
+        assertCanonicallyEqual("integerTestCanonicalization01", "referenceSnippet01");
+    }
+
+    public static void integerTestCanonicalization01(char a) {
+        if (Integer.compareUnsigned(a - 1, a) < 0) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet01(char a) {
+        if (a != 0) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Ignore("Needs better stamp support for unsigned ranges")
+    @Test
+    public void test1() {
+        assertCanonicallyEqual("integerTestCanonicalization1", "referenceSnippet1");
+    }
+
+    public static void integerTestCanonicalization1(char a) {
+        if (Integer.compareUnsigned(a - 2, a) < 0) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet1(char a) {
+        if (Integer.compareUnsigned(a, 2) >= 0) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test2() {
+        assertCanonicallyEqual("integerTestCanonicalization2", "referenceSnippet2");
+    }
+
+    public static void integerTestCanonicalization2(int a) {
+        if (a - 1 < a) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet2(int a) {
+        if (a != Integer.MIN_VALUE) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test3() {
+        assertCanonicallyEqual("integerTestCanonicalization3", "referenceSnippet3");
+    }
+
+    public static void integerTestCanonicalization3(int a) {
+        if (a - 2 < a) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet3(int a) {
+        if (a >= Integer.MIN_VALUE + 2) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test4() {
+        assertCanonicallyEqual("integerTestCanonicalization4", "referenceSnippet4");
+    }
+
+    public static void integerTestCanonicalization4(int a) {
+        if (a + 1 < a) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet4(int a) {
+        if (a == Integer.MAX_VALUE) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test5() {
+        assertCanonicallyEqual("integerTestCanonicalization5", "referenceSnippet5");
+    }
+
+    public static void integerTestCanonicalization5(int a) {
+        if (a + 2 < a) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet5(int a) {
+        if (a > Integer.MAX_VALUE - 2) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test6() {
+        assertCanonicallyEqual("integerTestCanonicalization6", "referenceSnippet6");
+    }
+
+    public static void integerTestCanonicalization6(int a) {
+        if (a < a + 1) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet6(int a) {
+        if (a != Integer.MAX_VALUE) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    @Test
+    public void test7() {
+        assertCanonicallyEqual("integerTestCanonicalization7", "referenceSnippet7");
+    }
+
+    public static void integerTestCanonicalization7(int a) {
+        if (a < a + 2) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    public static void referenceSnippet7(int a) {
+        if (a <= Integer.MAX_VALUE - 2) {
+            sink1 = 0;
+        } else {
+            sink0 = -1;
+        }
+    }
+
+    protected void assertCanonicallyEqual(String snippet, String reference) {
+        StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        PhaseContext context = new PhaseContext(getProviders());
+        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        canonicalizer.apply(graph, context);
+        canonicalizer.apply(graph, context);
+        StructuredGraph referenceGraph = parseEager(reference, AllowAssumptions.YES);
+        canonicalizer.apply(referenceGraph, context);
+        canonicalizer.apply(referenceGraph, context);
+        assertEquals(referenceGraph, graph, true, true);
+    }
+
+    @Override
+    protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
+        return InlineInvokePlugin.InlineInfo.createStandardInlineInfo(method);
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationMulTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationMulTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,8 +32,8 @@
 public class ConditionalEliminationMulTest extends GraalCompilerTest {
 
     public static void snippet01(int a) {
-        if (a == 2) {
-            if (a * 3 != 6) {
+        if (a == 3) {
+            if (a * 11 != 33) {
                 shouldBeOptimizedAway();
             }
         }
@@ -41,7 +41,7 @@
 
     public static void snippet02(int a) {
         if (a == 0) {
-            if (a * 3 != 0) {
+            if (a * 11 != 0) {
                 shouldBeOptimizedAway();
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest1.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest1.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,25 +22,25 @@
  */
 package org.graalvm.compiler.core.test;
 
+import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.junit.Test;
 
-import org.graalvm.compiler.api.directives.GraalDirectives;
-
 /**
  * Collection of tests for
  * {@link org.graalvm.compiler.phases.common.DominatorConditionalEliminationPhase} including those
  * that triggered bugs in this phase.
  */
 public class ConditionalEliminationTest1 extends ConditionalEliminationTestBase {
+    protected static int sink3;
 
     private static final String REFERENCE_SNIPPET = "referenceSnippet";
 
     @SuppressWarnings("all")
-    public static int referenceSnippet(int a) {
+    public static void referenceSnippet(int a) {
         if (a == 0) {
-            return 1;
+            sink1 = 1;
         }
-        return 0;
+        sink0 = 0;
     }
 
     @Test
@@ -49,21 +49,21 @@
     }
 
     @SuppressWarnings("all")
-    public static int test1Snippet(int a) {
+    public static void test1Snippet(int a) {
         if (a == 0) {
             if (a == 5) {
-                return 100;
+                sink2 = 100;
             }
             if (a > 100) {
                 if (a == 0) {
-                    return 200;
+                    sink3 = 200;
                 }
             }
             if (a != 2) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 0;
+        sink0 = 0;
     }
 
     @Test
@@ -72,18 +72,18 @@
     }
 
     @SuppressWarnings("all")
-    public static int test2Snippet(int a) {
+    public static void test2Snippet(int a) {
         if (a == 0) {
             if (a > 100) {
                 if (a == 0) {
-                    return 200;
+                    sink3 = 200;
                 }
             }
             if (a != 2) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 0;
+        sink0 = 0;
     }
 
     @Test
@@ -92,7 +92,7 @@
     }
 
     @SuppressWarnings("all")
-    public static int test3Snippet(int a) {
+    public static void test3Snippet(int a) {
         if (a == 0) {
             if (a < 1) {
                 if (a < 2) {
@@ -101,9 +101,9 @@
                             if (a > -2) {
                                 if (a > -3) {
                                     if (a == 1) {
-                                        return 42;
+                                        sink2 = 42;
                                     } else {
-                                        return 1;
+                                        sink1 = 1;
                                     }
                                 }
                             }
@@ -112,18 +112,18 @@
                 }
             }
         }
-        return 0;
+        sink0 = 0;
     }
 
     @SuppressWarnings("all")
-    public static int test4Snippet(int a, int b) {
+    public static void test4Snippet(int a, int b) {
         if (b < 1) {
             GraalDirectives.controlFlowAnchor();
             if (b < 0) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 0;
+        sink0 = 0;
     }
 
     @Test
@@ -132,21 +132,21 @@
     }
 
     @SuppressWarnings("all")
-    public static int test5Snippet(int a, int b) {
+    public static void test5Snippet(int a, int b) {
         if ((b & 3) == 0) {
             GraalDirectives.controlFlowAnchor();
             if ((b & 7) == 0) {
                 GraalDirectives.controlFlowAnchor();
-                return 1;
+                sink1 = 1;
             }
         } else {
             GraalDirectives.controlFlowAnchor();
             if ((b & 1) == 0) {
                 GraalDirectives.controlFlowAnchor();
-                return 2;
+                sink2 = 2;
             }
         }
-        return 0;
+        sink0 = 0;
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest11.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest11.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,22 +22,16 @@
  */
 package org.graalvm.compiler.core.test;
 
+import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import org.graalvm.compiler.api.directives.GraalDirectives;
-
 /**
  * Collection of tests for
  * {@link org.graalvm.compiler.phases.common.DominatorConditionalEliminationPhase} including those
  * that triggered bugs in this phase.
  */
 public class ConditionalEliminationTest11 extends ConditionalEliminationTestBase {
-    public ConditionalEliminationTest11() {
-        // Don't disable simplification
-        super(false);
-    }
-
     @SuppressWarnings("all")
     public static int referenceSnippet(int a) {
         if ((a & 15) != 15) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest13.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest13.java	Thu Apr 06 14:31:32 2017 -0700
@@ -34,13 +34,6 @@
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 public class ConditionalEliminationTest13 extends ConditionalEliminationTestBase {
-    public ConditionalEliminationTest13() {
-        super(false);
-    }
-
-    private static int sink0;
-    private static int sink1;
-    private static int sink2;
 
     @Override
     protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
@@ -317,7 +310,7 @@
         super.prepareGraph(graph, canonicalizer, context, applyLowering);
         graph.clearAllStateAfter();
         graph.setGuardsStage(StructuredGraph.GuardsStage.AFTER_FSA);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "After preparation");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "After preparation");
         canonicalizer.apply(graph, context);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest5.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest5.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,11 +22,10 @@
  */
 package org.graalvm.compiler.core.test;
 
+import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import org.graalvm.compiler.api.directives.GraalDirectives;
-
 /**
  * Collection of tests for
  * {@link org.graalvm.compiler.phases.common.DominatorConditionalEliminationPhase} including those
@@ -50,20 +49,20 @@
     static final class DistinctB {
     }
 
-    public static int reference1Snippet(Object a) {
+    public static void reference1Snippet(Object a) {
         if (a instanceof B) {
-            return 1;
+            sink1 = 1;
         }
-        return 2;
+        sink2 = 2;
     }
 
-    public static int test1Snippet(Object a) {
+    public static void test1Snippet(Object a) {
         if (a instanceof B) {
             if (a instanceof A) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 2;
+        sink2 = 2;
     }
 
     @Test
@@ -71,21 +70,21 @@
         testConditionalElimination("test1Snippet", "reference1Snippet");
     }
 
-    public static int reference2Snippet(A a) {
+    public static void reference2Snippet(A a) {
         if (a instanceof B) {
-            return 1;
+            sink1 = 1;
         }
-        return 2;
+        sink2 = 2;
     }
 
-    public static int test2Snippet(A a) {
+    public static void test2Snippet(A a) {
         if (a instanceof B) {
             B newVal = (B) a;
             if (newVal != null) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 2;
+        sink2 = 2;
     }
 
     @Test
@@ -94,28 +93,28 @@
     }
 
     @SuppressWarnings("unused")
-    public static int reference3Snippet(Object a, Object b) {
+    public static void reference3Snippet(Object a, Object b) {
         if (a instanceof DistinctA) {
             DistinctA proxyA = (DistinctA) a;
             if (b instanceof DistinctB) {
-                return 1;
+                sink1 = 1;
             }
         }
-        return 2;
+        sink2 = 2;
     }
 
     @SuppressWarnings("all")
-    public static int test3Snippet(Object a, Object b) {
+    public static void test3Snippet(Object a, Object b) {
         if (a instanceof DistinctA) {
             DistinctA proxyA = (DistinctA) a;
             if (b instanceof DistinctB) {
                 if (proxyA == b) {
-                    return 42;
+                    sink0 = 42;
                 }
-                return 1;
+                sink1 = 1;
             }
         }
-        return 2;
+        sink2 = 2;
     }
 
     @Test
@@ -123,54 +122,54 @@
         testConditionalElimination("test3Snippet", "reference3Snippet", true, false);
     }
 
-    public static int reference4Snippet(Object a) {
+    public static void reference4Snippet(Object a) {
         if (!(a instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
-    public static int test4Snippet1(Object a) {
+    public static void test4Snippet1(Object a) {
         if (!(a instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
         if (!(a instanceof A)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
-    public static int test4Snippet2(Object a) {
+    public static void test4Snippet2(Object a) {
         if (!(a instanceof A)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
         if (!(a instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
     @SuppressWarnings({"cast", "unused"})
-    public static int test4Snippet3(Object a) {
+    public static void test4Snippet3(Object a) {
         Object pi = (A) a;
         if (!(a instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
-    public static int test4Snippet4(Object a) {
+    public static void test4Snippet4(Object a) {
         if (!(a instanceof A)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
         if (!(((A) a) instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
     @SuppressWarnings({"cast"})
-    public static int test4Snippet5(Object a) {
+    public static void test4Snippet5(Object a) {
         Object pi = (A) a;
         if (pi == null) {
             GraalDirectives.deoptimizeAndInvalidate();
@@ -178,7 +177,7 @@
         if (!(a instanceof B)) {
             GraalDirectives.deoptimizeAndInvalidate();
         }
-        return 1;
+        sink1 = 1;
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -42,15 +42,9 @@
  * that triggered bugs in this phase.
  */
 public class ConditionalEliminationTestBase extends GraalCompilerTest {
-    private final boolean disableSimplification;
-
-    protected ConditionalEliminationTestBase() {
-        this(true);
-    }
-
-    protected ConditionalEliminationTestBase(boolean disableSimplification) {
-        this.disableSimplification = disableSimplification;
-    }
+    protected static int sink0;
+    protected static int sink1;
+    protected static int sink2;
 
     protected void testConditionalElimination(String snippet, String referenceSnippet) {
         testConditionalElimination(snippet, referenceSnippet, false, false);
@@ -59,15 +53,9 @@
     @SuppressWarnings("try")
     protected void testConditionalElimination(String snippet, String referenceSnippet, boolean applyConditionalEliminationOnReference, boolean applyLowering) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         PhaseContext context = new PhaseContext(getProviders());
         CanonicalizerPhase canonicalizer1 = new CanonicalizerPhase();
-        if (disableSimplification) {
-            /**
-             * Some tests break if simplification is done so only do it when needed.
-             */
-            canonicalizer1.disableSimplification();
-        }
         CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
         try (Debug.Scope scope = Debug.scope("ConditionalEliminationTest", graph)) {
             prepareGraph(graph, canonicalizer1, context, applyLowering);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DeMorganCanonicalizationTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017, 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 org.graalvm.compiler.core.test;
+
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.calc.AndNode;
+import org.graalvm.compiler.nodes.calc.NotNode;
+import org.graalvm.compiler.nodes.calc.OrNode;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DeMorganCanonicalizationTest extends GraalCompilerTest {
+
+    public static int or(int a, int b) {
+        return ~a | ~b;
+    }
+
+    public static int and(int a, int b) {
+        return ~a & ~b;
+    }
+
+    @Test
+    public void testAnd() {
+        StructuredGraph g = parseEager("and", AllowAssumptions.NO, getInitialOptions());
+        new CanonicalizerPhase().apply(g, getDefaultHighTierContext());
+        Assert.assertEquals(1, g.getNodes().filter(OrNode.class).count());
+        Assert.assertEquals(1, g.getNodes().filter(NotNode.class).count());
+
+        testAgainstExpected(g.method(), new Result(and(-1, 17), null), (Object) null, -1, 17);
+        testAgainstExpected(g.method(), new Result(and(-1, 1), null), (Object) null, -1, 1);
+        testAgainstExpected(g.method(), new Result(and(-1, -1), null), (Object) null, -1, -1);
+        testAgainstExpected(g.method(), new Result(and(Integer.MIN_VALUE, Integer.MIN_VALUE), null), (Object) null, Integer.MIN_VALUE, Integer.MIN_VALUE);
+    }
+
+    @Test
+    public void testOr() {
+        StructuredGraph g = parseEager("or", AllowAssumptions.NO, getInitialOptions());
+        new CanonicalizerPhase().apply(g, getDefaultHighTierContext());
+        Assert.assertEquals(1, g.getNodes().filter(AndNode.class).count());
+        Assert.assertEquals(1, g.getNodes().filter(NotNode.class).count());
+
+        testAgainstExpected(g.method(), new Result(or(-1, 17), null), (Object) null, -1, 17);
+        testAgainstExpected(g.method(), new Result(or(-1, 1), null), (Object) null, -1, 1);
+        testAgainstExpected(g.method(), new Result(or(-1, -1), null), (Object) null, -1, -1);
+        testAgainstExpected(g.method(), new Result(or(Integer.MIN_VALUE, Integer.MIN_VALUE), null), (Object) null, Integer.MIN_VALUE, Integer.MIN_VALUE);
+    }
+
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -86,9 +86,9 @@
             HighTierContext context = getDefaultHighTierContext();
             new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
             new CanonicalizerPhase().apply(graph, context);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
             StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.YES);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, referenceGraph, "ReferenceGraph");
+            Debug.dump(Debug.BASIC_LEVEL, referenceGraph, "ReferenceGraph");
             assertEquals(referenceGraph, graph);
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FindUniqueDefaultMethodTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FindUniqueDefaultMethodTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -142,7 +142,7 @@
         try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             compile(graph.method(), graph);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
+            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -82,7 +82,7 @@
                 }
             }
 
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "After lowering");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After lowering");
 
             Assert.assertNotNull(returnNode);
             Assert.assertNotNull(monitorexit);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -78,6 +78,7 @@
 import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
+import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -91,6 +92,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.nodes.java.AccessFieldNode;
 import org.graalvm.compiler.nodes.spi.LoweringProvider;
 import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
@@ -121,6 +123,7 @@
 import jdk.vm.ci.code.CodeCacheProvider;
 import jdk.vm.ci.code.InstalledCode;
 import jdk.vm.ci.code.TargetDescription;
+import jdk.vm.ci.meta.Assumptions.Assumption;
 import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.DeoptimizationReason;
 import jdk.vm.ci.meta.JavaKind;
@@ -130,7 +133,6 @@
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.ResolvedJavaType;
 import jdk.vm.ci.meta.SpeculationLog;
-import jdk.vm.ci.meta.Assumptions.Assumption;
 import jdk.vm.ci.services.Services;
 
 /**
@@ -410,13 +412,13 @@
         String mismatchString = compareGraphStrings(expected, expectedString, graph, actualString);
 
         if (!excludeVirtual && getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, expected, "Node count not matching - expected");
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Node count not matching - actual");
+            Debug.dump(Debug.BASIC_LEVEL, expected, "Node count not matching - expected");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "Node count not matching - actual");
             Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount() + "\n" + mismatchString);
         }
         if (!expectedString.equals(actualString)) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, expected, "mismatching graphs - expected");
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "mismatching graphs - actual");
+            Debug.dump(Debug.BASIC_LEVEL, expected, "mismatching graphs - expected");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "mismatching graphs - actual");
             Assert.fail(mismatchString);
         }
     }
@@ -482,22 +484,25 @@
 
         StringBuilder result = new StringBuilder();
         for (Block block : scheduleResult.getCFG().getBlocks()) {
-            result.append("Block " + block + " ");
+            result.append("Block ").append(block).append(' ');
             if (block == scheduleResult.getCFG().getStartBlock()) {
                 result.append("* ");
             }
             result.append("-> ");
             for (Block succ : block.getSuccessors()) {
-                result.append(succ + " ");
+                result.append(succ).append(' ');
             }
-            result.append("\n");
+            result.append('\n');
             for (Node node : scheduleResult.getBlockToNodesMap().get(block)) {
                 if (node instanceof ValueNode && node.isAlive()) {
-                    if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode)) {
+                    if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode || node instanceof ParameterNode)) {
                         if (node instanceof ConstantNode) {
                             String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
-                            String str = name + (excludeVirtual ? "\n" : "    (" + filteredUsageCount(node) + ")\n");
-                            constantsLines.add(str);
+                            if (excludeVirtual) {
+                                constantsLines.add(name);
+                            } else {
+                                constantsLines.add(name + "    (" + filteredUsageCount(node) + ")");
+                            }
                         } else {
                             int id;
                             if (canonicalId.get(node) != null) {
@@ -507,8 +512,17 @@
                                 canonicalId.set(node, id);
                             }
                             String name = node.getClass().getSimpleName();
-                            String str = "  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + filteredUsageCount(node) + ")\n");
-                            result.append(str);
+                            result.append("  ").append(id).append('|').append(name);
+                            if (node instanceof AccessFieldNode) {
+                                result.append('#');
+                                result.append(((AccessFieldNode) node).field());
+                            }
+                            if (!excludeVirtual) {
+                                result.append("    (");
+                                result.append(filteredUsageCount(node));
+                                result.append(')');
+                            }
+                            result.append('\n');
                         }
                     }
                 }
@@ -516,14 +530,14 @@
         }
 
         StringBuilder constantsLinesResult = new StringBuilder();
-        constantsLinesResult.append(constantsLines.size() + " constants:\n");
+        constantsLinesResult.append(constantsLines.size()).append(" constants:\n");
         Collections.sort(constantsLines);
         for (String s : constantsLines) {
             constantsLinesResult.append(s);
-            constantsLinesResult.append("\n");
+            constantsLinesResult.append('\n');
         }
 
-        return constantsLines.toString() + result.toString();
+        return constantsLinesResult.toString() + result.toString();
     }
 
     /**
@@ -545,15 +559,15 @@
         StringBuilder result = new StringBuilder();
         Block[] blocks = scheduleResult.getCFG().getBlocks();
         for (Block block : blocks) {
-            result.append("Block " + block + " ");
+            result.append("Block ").append(block).append(' ');
             if (block == scheduleResult.getCFG().getStartBlock()) {
                 result.append("* ");
             }
             result.append("-> ");
             for (Block succ : block.getSuccessors()) {
-                result.append(succ + " ");
+                result.append(succ).append(' ');
             }
-            result.append("\n");
+            result.append('\n');
             for (Node node : scheduleResult.getBlockToNodesMap().get(block)) {
                 result.append(String.format("%1S\n", node));
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -217,7 +217,7 @@
                 n.replaceFirstInput(param, constant);
             }
         }
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         for (FrameState fs : param.usages().filter(FrameState.class).snapshot()) {
             fs.replaceFirstInput(param, null);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LockEliminationTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LockEliminationTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,7 +25,8 @@
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 import org.junit.Test;
-
+import org.graalvm.compiler.loop.DefaultLoopPolicies;
+import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.MonitorExitNode;
@@ -91,6 +92,26 @@
         assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
     }
 
+    public void testUnrolledSyncSnippet(Object a) {
+        for (int i = 0; i < 3; i++) {
+            synchronized (a) {
+
+            }
+        }
+    }
+
+    @Test
+    public void testUnrolledSync() {
+        StructuredGraph graph = getGraph("testUnrolledSyncSnippet");
+        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        canonicalizer.apply(graph, new PhaseContext(getProviders()));
+        HighTierContext context = getDefaultHighTierContext();
+        new LoopFullUnrollPhase(canonicalizer, new DefaultLoopPolicies()).apply(graph, context);
+        new LockEliminationPhase().apply(graph);
+        assertDeepEquals(1, graph.getNodes().filter(RawMonitorEnterNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
+    }
+
     private StructuredGraph getGraph(String snippet) {
         ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
         StructuredGraph graph = parseEager(method, AllowAssumptions.YES);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -717,7 +717,7 @@
             if (mode == TestMode.WITHOUT_FRAMESTATES || mode == TestMode.INLINED_WITHOUT_FRAMESTATES) {
                 graph.clearAllStateAfter();
             }
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "after removal of framestates");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "after removal of framestates");
 
             new FloatingReadPhase().apply(graph);
             new RemoveValueProxyPhase().apply(graph);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -61,7 +61,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         assertDeepEquals(returnCount, graph.getNodes(ReturnNode.TYPE).count());
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NestedLoopTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NestedLoopTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -142,7 +142,7 @@
 
     private void test(String snippet, int rootExits, int nestedExits, int innerExits) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
 
         Assert.assertEquals(3, cfg.getLoops().size());
@@ -162,7 +162,7 @@
         Assert.assertEquals(rootExits, rootLoop.getExits().size());
         Assert.assertEquals(nestedExits, nestedLoop.getExits().size());
         Assert.assertEquals(innerExits, innerMostLoop.getExits().size());
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
     }
 
     private static boolean contains(Loop<Block> loop, Invoke node, ControlFlowGraph cfg) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -58,9 +58,11 @@
             x = 2;
             sideEffect = null;
         }
+        int b = 4;
         sideEffect = null;
+        int c = b % 5;
         // can shift
-        return a * x * 4;
+        return a * x * c;
     }
 
     public static int test2Snippet(int a) {
@@ -249,7 +251,7 @@
         gc2.apply(g2, htc);
         Debug.log("Test Graph Cost --> 1.Graph cost:%f vs. 2.Graph cost:%f\n", gc1.finalCycles, gc2.finalCycles);
         Assert.assertTrue(gc2.finalCycles > gc1.finalCycles);
-        Assert.assertTrue(gc2.finalSize == gc1.finalSize + 1/* mul has 3 const input */);
+        Assert.assertTrue(gc2.finalSize == gc1.finalSize);
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PhiCreationTests.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PhiCreationTests.java	Thu Apr 06 14:31:32 2017 -0700
@@ -70,7 +70,7 @@
     @Test
     public void test3() {
         StructuredGraph graph = parseEager("test3Snippet", AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         Assert.assertFalse(graph.getNodes().filter(ValuePhiNode.class).iterator().hasNext());
     }
 
@@ -86,7 +86,7 @@
     @Test
     public void test4() {
         StructuredGraph graph = parseEager("test4Snippet", AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         Assert.assertFalse(graph.getNodes().filter(ValuePhiNode.class).iterator().hasNext());
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -59,7 +59,7 @@
 
     private void test(String snippet, String reference) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         for (FrameState fs : graph.getNodes(FrameState.TYPE).snapshot()) {
             fs.replaceAtUsages(null);
             GraphUtil.killWithUnusedFloatingInputs(fs);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -94,7 +94,7 @@
             new FloatingReadPhase().apply(graph);
             canonicalizer.apply(graph, context);
 
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "After lowering");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After lowering");
 
             for (FloatingReadNode node : graph.getNodes(ParameterNode.TYPE).first().usages().filter(FloatingReadNode.class)) {
                 // Checking that the parameter a is not directly used for the access to field
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -131,7 +131,7 @@
     private void test(final String snippet, final String referenceSnippet) {
         // No debug scope to reduce console noise for @Test(expected = ...) tests
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         PhaseContext context = new PhaseContext(getProviders());
         new CanonicalizerPhase().apply(graph, context);
         StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.NO);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java	Thu Apr 06 14:31:32 2017 -0700
@@ -69,7 +69,7 @@
         BeginNode beginNode = graph.add(new BeginNode());
         returnNode.replaceAtPredecessor(beginNode);
         beginNode.setNext(returnNode);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         SchedulePhase schedulePhase = new SchedulePhase(SchedulingStrategy.EARLIEST);
         schedulePhase.apply(graph);
         ScheduleResult schedule = graph.getLastSchedule();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SimpleCFGTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SimpleCFGTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -41,7 +41,7 @@
 public class SimpleCFGTest extends GraalCompilerTest {
 
     private static void dumpGraph(final StructuredGraph graph) {
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -88,7 +88,7 @@
     private void test(final String snippet) {
         // No debug scope to reduce console noise for @Test(expected = ...) tests
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.YES);
         assertEquals(referenceGraph, graph);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -180,7 +180,7 @@
 
     private void test(String snippet, String referenceSnippet) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
         /*
          * When using FlowSensitiveReductionPhase instead of ConditionalEliminationPhase,
          * tail-duplication gets activated thus resulting in a graph with more nodes than the
@@ -200,8 +200,8 @@
     @Override
     protected void assertEquals(StructuredGraph expected, StructuredGraph graph) {
         if (getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, expected, "expected (node count)");
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "graph (node count)");
+            Debug.dump(Debug.BASIC_LEVEL, expected, "expected (node count)");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "graph (node count)");
             Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount());
         }
     }
@@ -244,7 +244,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
-        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph " + snippet);
+        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph " + snippet);
         Assert.assertFalse("shouldn't have nodes of type " + clazz, graph.getNodes().filter(clazz).iterator().hasNext());
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyDebugUsageTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyDebugUsageTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -83,9 +83,28 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "%s", graph.toString());
+            Debug.dump(Debug.BASIC_LEVEL, graph, "%s", graph.toString());
+        }
+    }
+
+    private static class InvalidDumpLevelPhase extends Phase {
+
+        @Override
+        protected void run(StructuredGraph graph) {
+            Debug.dump(Debug.VERY_DETAILED_LEVEL + 1, graph, "%s", graph);
+        }
+    }
+
+    private static class NonConstantDumpLevelPhase extends Phase {
+
+        @Override
+        protected void run(StructuredGraph graph) {
+            Debug.dump(getLevel(), graph, "%s", graph);
         }
 
+        int getLevel() {
+            return 10;
+        }
     }
 
     private static class InvalidVerifyUsagePhase extends Phase {
@@ -126,7 +145,7 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "error " + graph);
+            Debug.dump(Debug.BASIC_LEVEL, graph, "error " + graph);
         }
 
     }
@@ -169,7 +188,7 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "%s", graph);
+            Debug.dump(Debug.BASIC_LEVEL, graph, "%s", graph);
         }
 
     }
@@ -234,6 +253,16 @@
     }
 
     @Test(expected = VerificationError.class)
+    public void testDumpLevelInvalid() {
+        testDebugUsageClass(InvalidDumpLevelPhase.class);
+    }
+
+    @Test(expected = VerificationError.class)
+    public void testDumpNonConstantLevelInvalid() {
+        testDebugUsageClass(NonConstantDumpLevelPhase.class);
+    }
+
+    @Test(expected = VerificationError.class)
     public void testLogInvalidConcat() {
         testDebugUsageClass(InvalidConcatLogUsagePhase.class);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -135,6 +135,7 @@
     }
 
     public static Object[] array = new Object[]{1, 2, 3, 4, 5, "asdf", "asdf"};
+    public static char[] charArray = new char[]{1, 2, 3, 4, 5, 'a', 'f'};
 
     public static Object testArrayCopySnippet(int a) {
         Object[] tmp = new Object[]{a != 1 ? array[a] : null};
@@ -144,6 +145,18 @@
     }
 
     @Test
+    public void testPrimitiveArraycopy() {
+        testPartialEscapeAnalysis("testPrimitiveArraycopySnippet", 0, 0);
+    }
+
+    public static Object testPrimitiveArraycopySnippet(int a) {
+        char[] tmp = new char[]{a != 1 ? charArray[a] : 0};
+        char[] tmp2 = new char[5];
+        System.arraycopy(tmp, 0, tmp2, 4, 1);
+        return tmp2[4];
+    }
+
+    @Test
     @Ignore
     public void testCache() {
         testPartialEscapeAnalysis("testCacheSnippet", 0.75, 1);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -244,10 +244,10 @@
                                 ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
                                 : getDefaultGraphBuilderSuite();
                 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
-                Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+                Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
                 new CanonicalizerPhase().apply(graph, context);
                 new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
-                Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+                Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
                 new CanonicalizerPhase().apply(graph, context);
                 new DeadCodeEliminationPhase().apply(graph);
                 return graph;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -121,7 +121,7 @@
         long start = System.currentTimeMillis();
         phase.apply(g, context);
         long end = System.currentTimeMillis();
-        Debug.dump(Debug.DETAILED_LOG_LEVEL, g, "After %s", phase.contractorName());
+        Debug.dump(Debug.DETAILED_LEVEL, g, "After %s", phase.contractorName());
         return end - start;
     }
 
@@ -138,7 +138,7 @@
             next = callerGraph.getNodes(MethodCallTargetNode.TYPE).first().invoke();
             EconomicSet<Node> canonicalizeNodes = InliningUtil.inlineForCanonicalization(next, calleeGraph, false, calleeMethod);
             canonicalizer.applyIncremental(callerGraph, context, canonicalizeNodes);
-            Debug.dump(Debug.DETAILED_LOG_LEVEL, callerGraph, "After inlining %s into %s iteration %d", calleeMethod, callerMethod, i);
+            Debug.dump(Debug.DETAILED_LEVEL, callerGraph, "After inlining %s into %s iteration %d", calleeMethod, callerMethod, i);
         }
         return callerGraph;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/GraalTutorial.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/GraalTutorial.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,8 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.regex.Pattern;
+
 import org.graalvm.compiler.bytecode.Bytecode;
 import org.graalvm.compiler.bytecode.BytecodeDisassembler;
 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
@@ -56,7 +58,11 @@
         byte[] bytecodes = bytecode.getCode();
         Assert.assertNotNull(bytecodes);
 
-        System.out.println(new BytecodeDisassembler().disassemble(bytecode));
+        Pattern disassemblyLineRE = Pattern.compile(" *\\d+: [a-z][\\w_]+");
+        String disassembly = new BytecodeDisassembler().disassemble(bytecode);
+        for (String line : disassembly.split("\\n")) {
+            Assert.assertTrue(line, disassemblyLineRE.matcher(line).find());
+        }
     }
 
     /*
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java	Thu Apr 06 14:31:32 2017 -0700
@@ -39,6 +39,7 @@
 import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.debug.DebugTimer;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.MethodFilter;
 import org.graalvm.compiler.debug.internal.method.MethodMetricsRootScopeInfo;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.alloc.OutOfRegistersException;
@@ -175,11 +176,43 @@
             } catch (Throwable e) {
                 throw Debug.handle(e);
             }
+            checkForRequestedCrash(r.graph);
             return r.compilationResult;
         }
     }
 
     /**
+     * Checks whether the {@link GraalCompilerOptions#CrashAt} option indicates that the compilation
+     * of {@code graph} should result in an exception.
+     *
+     * @param graph a graph currently being compiled
+     * @throws RuntimeException if the value of {@link GraalCompilerOptions#CrashAt} matches
+     *             {@code graph.method()} or {@code graph.name}
+     */
+    private static void checkForRequestedCrash(StructuredGraph graph) {
+        String methodPattern = GraalCompilerOptions.CrashAt.getValue(graph.getOptions());
+        if (methodPattern != null) {
+            String crashLabel = null;
+            ResolvedJavaMethod method = graph.method();
+            if (method == null) {
+                if (graph.name.contains(methodPattern)) {
+                    crashLabel = graph.name;
+                }
+            } else {
+                MethodFilter[] filters = MethodFilter.parse(methodPattern);
+                for (MethodFilter filter : filters) {
+                    if (filter.matches(method)) {
+                        crashLabel = method.format("%H.%n(%p)");
+                    }
+                }
+            }
+            if (crashLabel != null) {
+                throw new RuntimeException("Forced crash after compiling " + crashLabel);
+            }
+        }
+    }
+
+    /**
      * Builds the graph, optimizes it.
      */
     @SuppressWarnings("try")
@@ -190,21 +223,25 @@
             if (graph.start().next() == null) {
                 graphBuilderSuite.apply(graph, highTierContext);
                 new DeadCodeEliminationPhase(DeadCodeEliminationPhase.Optionality.Optional).apply(graph);
+                Debug.dump(Debug.BASIC_LEVEL, graph, "After parsing");
             } else {
-                Debug.dump(Debug.INFO_LOG_LEVEL, graph, "initial state");
+                Debug.dump(Debug.INFO_LEVEL, graph, "initial state");
             }
 
             suites.getHighTier().apply(graph, highTierContext);
             graph.maybeCompress();
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After high tier");
 
             MidTierContext midTierContext = new MidTierContext(providers, target, optimisticOpts, profilingInfo);
             suites.getMidTier().apply(graph, midTierContext);
             graph.maybeCompress();
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After mid tier");
 
             LowTierContext lowTierContext = new LowTierContext(providers, target);
             suites.getLowTier().apply(graph, lowTierContext);
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After low tier");
 
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
+            Debug.dump(Debug.BASIC_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
         } catch (Throwable e) {
             throw Debug.handle(e);
         } finally {
@@ -269,7 +306,7 @@
                 linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 
                 lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder, graph.getOptions());
-                Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After linear scan order");
+                Debug.dump(Debug.INFO_LEVEL, lir, "After linear scan order");
             } catch (Throwable e) {
                 throw Debug.handle(e);
             }
@@ -283,9 +320,9 @@
             new LIRGenerationPhase().apply(backend.getTarget(), lirGenRes, context);
 
             try (Scope s = Debug.scope("LIRStages", nodeLirGen, lir)) {
-                Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "After LIR generation");
+                Debug.dump(Debug.BASIC_LEVEL, lir, "After LIR generation");
                 LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig, allocationRestrictedTo));
-                Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "Before code generation");
+                Debug.dump(Debug.BASIC_LEVEL, lir, "Before code generation");
                 return result;
             } catch (Throwable e) {
                 throw Debug.handle(e);
@@ -365,7 +402,7 @@
                 Debug.counter("ExceptionHandlersEmitted").add(compilationResult.getExceptionHandlers().size());
             }
 
-            Debug.dump(Debug.BASIC_LOG_LEVEL, compilationResult, "After code generation");
+            Debug.dump(Debug.BASIC_LEVEL, compilationResult, "After code generation");
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java	Thu Apr 06 14:31:32 2017 -0700
@@ -46,6 +46,8 @@
     public static final OptionKey<Boolean> ExitVMOnException = new OptionKey<>(false);
     @Option(help = "", type = OptionType.Debug)
     public static final OptionKey<Boolean> PrintStackTraceOnException = new OptionKey<>(false);
+    @Option(help = "Pattern (see MethodFilter for format) for method that will trigger an exception when compiled. " +
+                   "This option exists to test handling compilation crashes gracefully.", type = OptionType.Debug)
+    public static final OptionKey<String> CrashAt = new OptionKey<>(null);
     // @formatter:on
-
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -91,12 +91,12 @@
             listener = new HashSetNodeEventListener();
             try (NodeEventScope s = graph.trackNodeEvents(listener)) {
                 try (Scope s2 = Debug.scope("WithGraphChangeMonitoring")) {
-                    if (Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
-                        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "*** Before phase %s", getName());
+                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
+                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** Before phase %s", getName());
                     }
                     super.run(graph, context);
-                    if (Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
-                        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "*** After phase %s %s", getName(), filteredNodes);
+                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
+                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** After phase %s %s", getName(), filteredNodes);
                     }
                     Debug.log("*** %s %s %s\n", message, graph, filteredNodes);
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java	Thu Apr 06 14:31:32 2017 -0700
@@ -122,11 +122,41 @@
         return config.isDumpEnabledForMethod();
     }
 
-    public static final int BASIC_LOG_LEVEL = 1;
-    public static final int INFO_LOG_LEVEL = 2;
-    public static final int VERBOSE_LOG_LEVEL = 3;
-    public static final int DETAILED_LOG_LEVEL = 4;
-    public static final int VERY_DETAILED_LOG_LEVEL = 5;
+    /**
+     * Basic debug level.
+     *
+     * For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
+     * after mid tier, after low tier.
+     */
+    public static final int BASIC_LEVEL = 1;
+
+    /**
+     * Informational debug level.
+     *
+     * HIR dumping: One graph after each applied top-level phase.
+     */
+    public static final int INFO_LEVEL = 2;
+
+    /**
+     * Verbose debug level.
+     *
+     * HIR dumping: One graph after each phase (including sub phases).
+     */
+    public static final int VERBOSE_LEVEL = 3;
+
+    /**
+     * Detailed debug level.
+     *
+     * HIR dumping: Graphs within phases where interesting for a phase, max ~5 per phase.
+     */
+    public static final int DETAILED_LEVEL = 4;
+
+    /**
+     * Very detailed debug level.
+     *
+     * HIR dumping: Graphs per node granularity graph change (before/after change).
+     */
+    public static final int VERY_DETAILED_LEVEL = 5;
 
     public static boolean isDumpEnabled(int dumpLevel) {
         return ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel);
@@ -183,7 +213,7 @@
     }
 
     public static boolean isLogEnabled() {
-        return isLogEnabled(BASIC_LOG_LEVEL);
+        return isLogEnabled(BASIC_LEVEL);
     }
 
     public static boolean isLogEnabled(int logLevel) {
@@ -420,7 +450,7 @@
     }
 
     public static void log(String msg) {
-        log(BASIC_LOG_LEVEL, msg);
+        log(BASIC_LEVEL, msg);
     }
 
     /**
@@ -435,7 +465,7 @@
     }
 
     public static void log(String format, Object arg) {
-        log(BASIC_LOG_LEVEL, format, arg);
+        log(BASIC_LEVEL, format, arg);
     }
 
     /**
@@ -451,7 +481,7 @@
     }
 
     public static void log(String format, int arg) {
-        log(BASIC_LOG_LEVEL, format, arg);
+        log(BASIC_LEVEL, format, arg);
     }
 
     /**
@@ -467,7 +497,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2);
+        log(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -480,7 +510,7 @@
     }
 
     public static void log(String format, int arg1, Object arg2) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2);
+        log(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -493,7 +523,7 @@
     }
 
     public static void log(String format, Object arg1, int arg2) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2);
+        log(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -506,7 +536,7 @@
     }
 
     public static void log(String format, int arg1, int arg2) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2);
+        log(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -519,7 +549,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3);
     }
 
     /**
@@ -532,7 +562,7 @@
     }
 
     public static void log(String format, int arg1, int arg2, int arg3) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3);
     }
 
     /**
@@ -545,7 +575,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
     }
 
     /**
@@ -558,7 +588,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
     }
 
     /**
@@ -571,7 +601,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
     }
 
     /**
@@ -584,11 +614,11 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
     }
 
     /**
@@ -607,7 +637,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
     }
 
     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
@@ -617,7 +647,7 @@
     }
 
     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
-        log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
     }
 
     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
@@ -627,7 +657,7 @@
     }
 
     public static void logv(String format, Object... args) {
-        logv(BASIC_LOG_LEVEL, format, args);
+        logv(BASIC_LEVEL, format, args);
     }
 
     /**
@@ -655,7 +685,7 @@
     @Deprecated
     public static void log(String format, Object[] args) {
         assert false : "shouldn't use this";
-        log(BASIC_LOG_LEVEL, format, args);
+        log(BASIC_LEVEL, format, args);
     }
 
     /**
@@ -670,6 +700,14 @@
         logv(logLevel, format, args);
     }
 
+    /**
+     * Forces an unconditional dump. This method exists mainly for debugging. It can also be used to
+     * force a graph dump from IDEs that support invoking a Java method while at a breakpoint.
+     */
+    public static void forceDump(Object object, String format, Object... args) {
+        DebugScope.forceDump(object, format, args);
+    }
+
     public static void dump(int dumpLevel, Object object, String msg) {
         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
             DebugScope.getInstance().dump(dumpLevel, object, msg);
@@ -771,7 +809,7 @@
     }
 
     public static Indent logAndIndent(String msg) {
-        return logAndIndent(BASIC_LOG_LEVEL, msg);
+        return logAndIndent(BASIC_LEVEL, msg);
     }
 
     /**
@@ -789,7 +827,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg);
+        return logAndIndent(BASIC_LEVEL, format, arg);
     }
 
     /**
@@ -808,7 +846,7 @@
     }
 
     public static Indent logAndIndent(String format, int arg) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg);
+        return logAndIndent(BASIC_LEVEL, format, arg);
     }
 
     /**
@@ -827,7 +865,7 @@
     }
 
     public static Indent logAndIndent(String format, int arg1, Object arg2) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -841,7 +879,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, int arg2) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -855,7 +893,7 @@
     }
 
     public static Indent logAndIndent(String format, int arg1, int arg2) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -869,7 +907,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, Object arg2) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
     }
 
     /**
@@ -883,7 +921,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
     }
 
     /**
@@ -897,7 +935,7 @@
     }
 
     public static Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
     }
 
     /**
@@ -911,7 +949,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
     }
 
     /**
@@ -925,7 +963,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
     }
 
     /**
@@ -939,7 +977,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
     }
 
     /**
@@ -953,7 +991,7 @@
     }
 
     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
     }
 
     /**
@@ -1001,7 +1039,7 @@
     @Deprecated
     public static void logAndIndent(String format, Object[] args) {
         assert false : "shouldn't use this";
-        logAndIndent(BASIC_LOG_LEVEL, format, args);
+        logAndIndent(BASIC_LEVEL, format, args);
     }
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java	Thu Apr 06 14:31:32 2017 -0700
@@ -41,28 +41,28 @@
  * A filter is a list of comma-separated terms of the form {@code <pattern>[:<level>]}. {@code
  * <pattern>} is interpreted as a glob pattern if it contains a "*" or "?" character. Otherwise, it
  * is interpreted as a substring. If {@code <pattern>} is empty, it matches every scope. If {@code :
- * <level>} is omitted, it defaults to {@link Debug#BASIC_LOG_LEVEL}. The term {@code ~<pattern>} is
- * a shorthand for {@code <pattern>:0} to disable a debug facility for a pattern.
+ * <level>} is omitted, it defaults to {@link Debug#BASIC_LEVEL}. The term {@code ~<pattern>} is a
+ * shorthand for {@code <pattern>:0} to disable a debug facility for a pattern.
  * <p>
  * The resulting log level of a scope is determined by the <em>last</em> matching term. If no term
  * matches, the log level is 0 (disabled). A filter with no terms matches every scope with a log
- * level of {@link Debug#BASIC_LOG_LEVEL}.
+ * level of {@link Debug#BASIC_LEVEL}.
  *
  * <h2>Examples of filters</h2>
  *
  * <ul>
  * <li>(empty string)<br>
- * Matches any scope with log level {@link Debug#BASIC_LOG_LEVEL}.
+ * Matches any scope with log level {@link Debug#BASIC_LEVEL}.
  *
  * <li>{@code :1}<br>
  * Matches any scope with log level 1.
  *
  * <li>{@code *}<br>
- * Matches any scope with log level {@link Debug#BASIC_LOG_LEVEL}.
+ * Matches any scope with log level {@link Debug#BASIC_LEVEL}.
  *
  * <li>{@code CodeGen,CodeInstall}<br>
  * Matches scopes containing "CodeGen" or "CodeInstall", both with log level
- * {@link Debug#BASIC_LOG_LEVEL}.
+ * {@link Debug#BASIC_LEVEL}.
  *
  * <li>{@code CodeGen:2,CodeInstall:1}<br>
  * Matches scopes containing "CodeGen" with log level 2, or "CodeInstall" with log level 1.
@@ -74,10 +74,10 @@
  * Matches all scopes with log level 1, except those containing "Dead".
  *
  * <li>{@code Code*}<br>
- * Matches scopes starting with "Code" with log level {@link Debug#BASIC_LOG_LEVEL}.
+ * Matches scopes starting with "Code" with log level {@link Debug#BASIC_LEVEL}.
  *
  * <li>{@code Code,~Dead}<br>
- * Matches scopes containing "Code" but not "Dead", with log level {@link Debug#BASIC_LOG_LEVEL}.
+ * Matches scopes containing "Code" but not "Dead", with log level {@link Debug#BASIC_LEVEL}.
  * </ul>
  */
 final class DebugFilter {
@@ -108,7 +108,7 @@
                         level = 0;
                     } else {
                         pattern = t;
-                        level = Debug.BASIC_LOG_LEVEL;
+                        level = Debug.BASIC_LEVEL;
                     }
                 } else {
                     pattern = t.substring(0, idx);
@@ -119,13 +119,13 @@
                         } catch (NumberFormatException e) {
                             switch (levelString) {
                                 case "basic":
-                                    level = Debug.BASIC_LOG_LEVEL;
+                                    level = Debug.BASIC_LEVEL;
                                     break;
                                 case "info":
-                                    level = Debug.INFO_LOG_LEVEL;
+                                    level = Debug.INFO_LEVEL;
                                     break;
                                 case "verbose":
-                                    level = Debug.VERBOSE_LOG_LEVEL;
+                                    level = Debug.VERBOSE_LEVEL;
                                     break;
                                 default:
                                     throw new IllegalArgumentException("Unknown dump level: \"" + levelString + "\" expected basic, info, verbose or an integer");
@@ -133,7 +133,7 @@
                         }
 
                     } else {
-                        level = Debug.BASIC_LOG_LEVEL;
+                        level = Debug.BASIC_LEVEL;
                     }
                 }
 
@@ -147,7 +147,7 @@
      */
     public int matchLevel(String input) {
         if (terms == null) {
-            return Debug.BASIC_LOG_LEVEL;
+            return Debug.BASIC_LEVEL;
         } else {
             int level = 0;
             for (Term t : terms) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugRetryableTask.java	Thu Apr 06 14:31:32 2017 -0700
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, 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 org.graalvm.compiler.debug;
+
+import org.graalvm.compiler.debug.Debug.Scope;
+
+/**
+ * A mechanism for re-executing a task upon failure.
+ */
+public abstract class DebugRetryableTask<T> extends DelegatingDebugConfig {
+
+    /**
+     * Calls {@link #run} on this task and if it results in an exception, calls
+     * {@link #onRetry(Throwable)} and if that returns {@code true}, calls {@link #run}.
+     */
+    @SuppressWarnings("try")
+    public final T execute() {
+        try {
+            return run(null);
+        } catch (Throwable t) {
+            if (onRetry(t)) {
+                try (Scope d = Debug.sandbox("Retrying: " + this, this)) {
+                    return run(t);
+                } catch (Throwable t2) {
+                    throw Debug.handle(t2);
+                }
+            } else {
+                throw t;
+            }
+        }
+    }
+
+    /**
+     * Runs this task.
+     *
+     * @param failure the cause of the first execution to fail or {@code null} if this is the first
+     *            execution of {@link #run(Throwable)}
+     */
+    protected abstract T run(Throwable failure);
+
+    /**
+     * Notifies this object that the initial execution failed with exception {@code t} and is about
+     * to be re-executed. The re-execution will use this object as the active {@link DebugConfig}.
+     * As such, this method can be overridden to enable more detailed debug facilities.
+     *
+     * @param t an exception that terminated the first execution of this task
+     * @return whether this task should be re-executed. If false, {@code t} will be re-thrown.
+     */
+    protected boolean onRetry(Throwable t) {
+        return true;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GraalDebugConfig.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GraalDebugConfig.java	Thu Apr 06 14:31:32 2017 -0700
@@ -79,7 +79,9 @@
         @Option(help = "Write debug values into a file instead of the terminal. " +
                        "If DebugValueSummary is Thread, the thread name will be prepended.", type = OptionType.Debug)
         public static final OptionKey<String> DebugValueFile = new OptionKey<>(null);
-        @Option(help = "Send Graal compiler IR to dump handlers on error", type = OptionType.Debug)
+        @Option(help = "Enable debug output for stub code generation and snippet preparation.", type = OptionType.Debug)
+        public static final OptionKey<Boolean> DebugStubsAndSnippets = new OptionKey<>(false);
+        @Option(help = "Send Graal compiler IR to dump handlers on error.", type = OptionType.Debug)
         public static final OptionKey<Boolean> DumpOnError = new OptionKey<>(false);
         @Option(help = "Intercept also bailout exceptions", type = OptionType.Debug)
         public static final OptionKey<Boolean> InterceptBailout = new OptionKey<>(false);
@@ -406,7 +408,7 @@
         if (e instanceof BailoutException && !Options.InterceptBailout.getValue(options)) {
             return null;
         }
-        Debug.setConfig(Debug.fixedConfig(options, Debug.BASIC_LOG_LEVEL, Debug.BASIC_LOG_LEVEL, false, false, false, false, false, dumpHandlers, verifyHandlers, output));
+        Debug.setConfig(Debug.fixedConfig(options, Debug.BASIC_LEVEL, Debug.BASIC_LEVEL, false, false, false, false, false, dumpHandlers, verifyHandlers, output));
         Debug.log("Exception occurred in scope: %s", Debug.currentScope());
         Map<Object, Object> firstSeen = new IdentityHashMap<>();
         for (Object o : Debug.context()) {
@@ -414,7 +416,7 @@
             if (!firstSeen.containsKey(o)) {
                 firstSeen.put(o, o);
                 if (Options.DumpOnError.getValue(options) || Options.Dump.getValue(options) != null) {
-                    Debug.dump(Debug.BASIC_LOG_LEVEL, o, "Exception: %s", e);
+                    Debug.dump(Debug.BASIC_LEVEL, o, "Exception: %s", e);
                 } else {
                     Debug.log("Context obj %s", o);
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java	Thu Apr 06 14:31:32 2017 -0700
@@ -352,7 +352,7 @@
                 dumpHandler.dump(object, message);
             }
         } else {
-            TTY.println("Forced dump ignored because debugging is disabled - use -Dgraal.Dump=xxx");
+            TTY.println("Forced dump ignored because debugging is disabled - use -Dgraal.ForceDebugEnable=true");
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java	Thu Apr 06 14:31:32 2017 -0700
@@ -405,6 +405,13 @@
         return add(node);
     }
 
+    public <T extends Node> T maybeAddOrUnique(T node) {
+        if (node.isAlive()) {
+            return node;
+        }
+        return addOrUnique(node);
+    }
+
     public <T extends Node> T addOrUniqueWithInputs(T node) {
         if (node.isAlive()) {
             assert node.graph() == this;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Thu Apr 06 14:31:32 2017 -0700
@@ -40,6 +40,8 @@
 import java.util.function.Predicate;
 
 import org.graalvm.compiler.core.common.Fields;
+import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.DebugCloseable;
 import org.graalvm.compiler.debug.Fingerprint;
 import org.graalvm.compiler.graph.Graph.NodeEvent;
@@ -131,7 +133,9 @@
      * Denotes an injected parameter in a {@linkplain NodeIntrinsic node intrinsic} constructor. If
      * the constructor is called as part of node intrinsification, the node intrinsifier will inject
      * an argument for the annotated parameter. Injected parameters must precede all non-injected
-     * parameters in a constructor.
+     * parameters in a constructor. If the type of the annotated parameter is {@link Stamp}, the
+     * {@linkplain Stamp#javaType type} of the injected stamp is the return type of the annotated
+     * method (which cannot be {@code void}).
      */
     @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
     @java.lang.annotation.Target(ElementType.PARAMETER)
@@ -140,40 +144,45 @@
 
     /**
      * Annotates a method that can be replaced by a compiler intrinsic. A (resolved) call to the
-     * annotated method can be replaced with an instance of the node class denoted by
-     * {@link #value()}. For this reason, the signature of the annotated method must match the
-     * signature (excluding a prefix of {@linkplain InjectedNodeParameter injected} parameters) of a
-     * constructor in the node class.
+     * annotated method will be processed by a generated {@code InvocationPlugin} that calls either
+     * a factory method or a constructor corresponding with the annotated method.
      * <p>
-     * If the node class has a static method {@code intrinsify} with a matching signature plus a
-     * {@code GraphBuilderContext} as first argument, this method is called instead of creating the
-     * node.
+     * A factory method corresponding to an annotated method is a static method named
+     * {@code intrinsify} defined in the class denoted by {@link #value()}. In order, its signature
+     * is as follows:
+     * <ol>
+     * <li>A {@code GraphBuilderContext} parameter.</li>
+     * <li>A {@code ResolvedJavaMethod} parameter.</li>
+     * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li>
+     * <li>Remaining parameters that match the declared parameters of the annotated method.</li>
+     * </ol>
+     * A constructor corresponding to an annotated method is defined in the class denoted by
+     * {@link #value()}. In order, its signature is as follows:
+     * <ol>
+     * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li>
+     * <li>Remaining parameters that match the declared parameters of the annotated method.</li>
+     * </ol>
+     * There must be exactly one such factory method or constructor corresponding to a
+     * {@link NodeIntrinsic} annotated method.
      */
     @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
     @java.lang.annotation.Target(ElementType.METHOD)
     public static @interface NodeIntrinsic {
 
         /**
-         * Gets the {@link Node} subclass instantiated when intrinsifying a call to the annotated
-         * method. If not specified, then the class in which the annotated method is declared is
-         * used (and is assumed to be a {@link Node} subclass).
+         * The class declaring the factory method or {@link Node} subclass declaring the constructor
+         * used to intrinsify a call to the annotated method. The default value is the class in
+         * which the annotated method is declared.
          */
         Class<?> value() default NodeIntrinsic.class;
 
         /**
-         * Determines if the stamp of the instantiated intrinsic node has its stamp set from the
-         * return type of the annotated method.
-         * <p>
-         * When it is set to true, the stamp that is passed in to the constructor of ValueNode is
-         * ignored and can therefore safely be {@code null}.
+         * If {@code true}, the factory method or constructor selected by the annotation must have
+         * an {@linkplain InjectedNodeParameter injected} {@link Stamp} parameter. Calling
+         * {@link AbstractPointerStamp#nonNull()} on the injected stamp is guaranteed to return
+         * {@code true}.
          */
-        boolean setStampFromReturnType() default false;
-
-        /**
-         * Determines if the stamp of the instantiated intrinsic node is guaranteed to be non-null.
-         * Generally used in conjunction with {@link #setStampFromReturnType()}.
-         */
-        boolean returnStampIsNonNull() default false;
+        boolean injectedStampIsNonNull() default false;
     }
 
     /**
@@ -304,7 +313,7 @@
      * @return an {@link NodeIterable iterable} for all non-null successor edges.
      */
     public NodeIterable<Node> successors() {
-        assert !this.isDeleted();
+        assert !this.isDeleted() : this;
         return nodeClass.getSuccessorIterable(this);
     }
 
@@ -328,7 +337,7 @@
         if (usage1 == null) {
             return 1;
         }
-        return 2 + extraUsagesCount;
+        return INLINE_USAGE_COUNT + extraUsagesCount;
     }
 
     /**
@@ -391,30 +400,45 @@
     }
 
     private void movUsageFromEndTo(int destIndex) {
-        int lastIndex = this.getUsageCount() - 1;
-        if (destIndex == 0) {
-            if (lastIndex == 0) {
-                usage0 = null;
-                return;
-            } else if (lastIndex == 1) {
-                usage0 = usage1;
-                usage1 = null;
-                return;
-            } else {
-                usage0 = extraUsages[lastIndex - INLINE_USAGE_COUNT];
-            }
+        if (destIndex >= INLINE_USAGE_COUNT) {
+            movUsageFromEndToExtraUsages(destIndex - INLINE_USAGE_COUNT);
         } else if (destIndex == 1) {
-            if (lastIndex == 1) {
-                usage1 = null;
-                return;
-            }
-            usage1 = extraUsages[lastIndex - INLINE_USAGE_COUNT];
+            movUsageFromEndToIndexOne();
         } else {
-            Node n = extraUsages[lastIndex - INLINE_USAGE_COUNT];
-            extraUsages[destIndex - INLINE_USAGE_COUNT] = n;
+            assert destIndex == 0;
+            movUsageFromEndToIndexZero();
         }
-        extraUsages[lastIndex - INLINE_USAGE_COUNT] = null;
+    }
+
+    private void movUsageFromEndToExtraUsages(int destExtraIndex) {
         this.extraUsagesCount--;
+        Node n = extraUsages[extraUsagesCount];
+        extraUsages[destExtraIndex] = n;
+        extraUsages[extraUsagesCount] = null;
+    }
+
+    private void movUsageFromEndToIndexZero() {
+        if (extraUsagesCount > 0) {
+            this.extraUsagesCount--;
+            usage0 = extraUsages[extraUsagesCount];
+            extraUsages[extraUsagesCount] = null;
+        } else if (usage1 != null) {
+            usage0 = usage1;
+            usage1 = null;
+        } else {
+            usage0 = null;
+        }
+    }
+
+    private void movUsageFromEndToIndexOne() {
+        if (extraUsagesCount > 0) {
+            this.extraUsagesCount--;
+            usage1 = extraUsages[extraUsagesCount];
+            extraUsages[extraUsagesCount] = null;
+        } else {
+            assert usage1 != null;
+            usage1 = null;
+        }
     }
 
     /**
@@ -425,20 +449,21 @@
      */
     public boolean removeUsage(Node node) {
         assert node != null;
-        // It is critical that this method maintains the invariant that
-        // the usage list has no null element preceding a non-null element
+        // For large graphs, usage removal is performance critical.
+        // Furthermore, it is critical that this method maintains the invariant that the usage list
+        // has no null element preceding a non-null element.
         incUsageModCount();
         if (usage0 == node) {
-            this.movUsageFromEndTo(0);
+            movUsageFromEndToIndexZero();
             return true;
         }
         if (usage1 == node) {
-            this.movUsageFromEndTo(1);
+            movUsageFromEndToIndexOne();
             return true;
         }
         for (int i = this.extraUsagesCount - 1; i >= 0; i--) {
             if (extraUsages[i] == node) {
-                this.movUsageFromEndTo(i + INLINE_USAGE_COUNT);
+                movUsageFromEndToExtraUsages(i);
                 return true;
             }
         }
@@ -537,8 +562,9 @@
         assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id);
         this.graph = newGraph;
         newGraph.register(this);
-        this.getNodeClass().registerAtInputsAsUsage(this);
-        this.getNodeClass().registerAtSuccessorsAsPredecessor(this);
+        NodeClass<? extends Node> nc = nodeClass;
+        nc.registerAtInputsAsUsage(this);
+        nc.registerAtSuccessorsAsPredecessor(this);
     }
 
     /**
@@ -588,7 +614,7 @@
     }
 
     public final void replaceAtUsages(Node other) {
-        replaceAtUsages(other, null, null);
+        replaceAtAllUsages(other, (Node) null);
     }
 
     public final void replaceAtUsages(Node other, Predicate<Node> filter) {
@@ -606,22 +632,60 @@
     }
 
     protected void replaceAtUsages(Node other, Predicate<Node> filter, Node toBeDeleted) {
+        if (filter == null) {
+            replaceAtAllUsages(other, toBeDeleted);
+        } else {
+            replaceAtMatchingUsages(other, filter, toBeDeleted);
+        }
+    }
+
+    protected void replaceAtAllUsages(Node other, Node toBeDeleted) {
+        assert checkReplaceWith(other);
+        if (usage0 == null) {
+            return;
+        }
+        replaceAtUsage(other, toBeDeleted, usage0);
+        usage0 = null;
+
+        if (usage1 == null) {
+            return;
+        }
+        replaceAtUsage(other, toBeDeleted, usage1);
+        usage1 = null;
+
+        if (extraUsagesCount <= 0) {
+            return;
+        }
+        for (int i = 0; i < extraUsagesCount; i++) {
+            Node usage = extraUsages[i];
+            replaceAtUsage(other, toBeDeleted, usage);
+        }
+        this.extraUsages = NO_NODES;
+        this.extraUsagesCount = 0;
+    }
+
+    private void replaceAtUsage(Node other, Node toBeDeleted, Node usage) {
+        boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
+        assert assertTrue(result, "not found in inputs, usage: %s", usage);
+        /*
+         * Don't notify for nodes which are about to be deleted.
+         */
+        if (toBeDeleted == null || usage != toBeDeleted) {
+            maybeNotifyInputChanged(usage);
+        }
+        if (other != null) {
+            other.addUsage(usage);
+        }
+    }
+
+    private void replaceAtMatchingUsages(Node other, Predicate<Node> filter, Node toBeDeleted) {
+        assert filter != null;
         assert checkReplaceWith(other);
         int i = 0;
         while (i < this.getUsageCount()) {
             Node usage = this.getUsageAt(i);
             if (filter == null || filter.test(usage)) {
-                boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
-                assert assertTrue(result, "not found in inputs, usage: %s", usage);
-                /*
-                 * Don't notify for nodes which are about to be deleted.
-                 */
-                if (toBeDeleted == null || usage != toBeDeleted) {
-                    maybeNotifyInputChanged(usage);
-                }
-                if (other != null) {
-                    other.addUsage(usage);
-                }
+                replaceAtUsage(other, toBeDeleted, usage);
                 this.movUsageFromEndTo(i);
             } else {
                 ++i;
@@ -641,21 +705,7 @@
 
     public void replaceAtMatchingUsages(Node other, NodePredicate usagePredicate) {
         assert checkReplaceWith(other);
-        int index = 0;
-        while (index < this.getUsageCount()) {
-            Node usage = getUsageAt(index);
-            if (usagePredicate.apply(usage)) {
-                boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
-                assert assertTrue(result, "not found in inputs, usage: %s", usage);
-                if (other != null) {
-                    maybeNotifyInputChanged(usage);
-                    other.addUsage(usage);
-                }
-                this.movUsageFromEndTo(index);
-            } else {
-                index++;
-            }
-        }
+        replaceAtMatchingUsages(other, usagePredicate, null);
     }
 
     public void replaceAtUsages(InputType type, Node other) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeBitMap.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeBitMap.java	Thu Apr 06 14:31:32 2017 -0700
@@ -154,7 +154,7 @@
         if (bits.length < other.bits.length) {
             bits = Arrays.copyOf(bits, other.bits.length);
         }
-        for (int i = 0; i < bits.length; i++) {
+        for (int i = 0; i < Math.min(bits.length, other.bits.length); i++) {
             bits[i] |= other.bits[i];
         }
     }
@@ -181,44 +181,47 @@
         }
     }
 
-    private static class MarkedNodeIterator implements Iterator<Node> {
+    protected int nextMarkedNodeId(int fromNodeId) {
+        assert fromNodeId >= 0;
+        int wordIndex = fromNodeId >> SHIFT;
+        int wordsInUse = bits.length;
+        if (wordIndex < wordsInUse) {
+            long word = bits[wordIndex] & (0xFFFFFFFFFFFFFFFFL << fromNodeId);
+            while (true) {
+                if (word != 0) {
+                    return wordIndex * Long.SIZE + Long.numberOfTrailingZeros(word);
+                }
+                if (++wordIndex == wordsInUse) {
+                    break;
+                }
+                word = bits[wordIndex];
+            }
+        }
+        return -2;
+    }
 
-        private final NodeBitMap visited;
-        private Iterator<Node> nodes;
-        private Node nextNode;
+    private class MarkedNodeIterator implements Iterator<Node> {
+        private int nextNodeId;
 
-        MarkedNodeIterator(NodeBitMap visited, Iterator<Node> nodes) {
-            this.visited = visited;
-            this.nodes = nodes;
+        MarkedNodeIterator() {
+            nextNodeId = -1;
             forward();
         }
 
         private void forward() {
-            do {
-                if (!nodes.hasNext()) {
-                    nextNode = null;
-                    return;
-                }
-                nextNode = nodes.next();
-                if (visited.isNew(nextNode)) {
-                    nextNode = null;
-                    return;
-                }
-            } while (!visited.isMarked(nextNode));
+            nextNodeId = NodeBitMap.this.nextMarkedNodeId(nextNodeId + 1);
         }
 
         @Override
         public boolean hasNext() {
-            return nextNode != null;
+            return nextNodeId >= 0;
         }
 
         @Override
         public Node next() {
-            try {
-                return nextNode;
-            } finally {
-                forward();
-            }
+            Node result = graph.getNode(nextNodeId);
+            forward();
+            return result;
         }
 
         @Override
@@ -230,7 +233,7 @@
 
     @Override
     public Iterator<Node> iterator() {
-        return new MarkedNodeIterator(NodeBitMap.this, graph().getNodes().iterator());
+        return new MarkedNodeIterator();
     }
 
     public NodeBitMap copy() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java	Thu Apr 06 14:31:32 2017 -0700
@@ -599,23 +599,24 @@
     }
 
     private static boolean deepEquals0(Object e1, Object e2) {
-        assert e1 != null;
-        if (e2 == null) {
+        if (e1 == e2) {
+            return true;
+        } else if (e1 == null || e2 == null) {
             return false;
-        } else if (e1 instanceof Object[] && e2 instanceof Object[]) {
-            return Arrays.deepEquals((Object[]) e1, (Object[]) e2);
         } else if (!e1.getClass().isArray() || e1.getClass() != e2.getClass()) {
             return e1.equals(e2);
-        } else if (e1 instanceof byte[]) {
-            return Arrays.equals((byte[]) e1, (byte[]) e2);
-        } else if (e1 instanceof short[]) {
-            return Arrays.equals((short[]) e1, (short[]) e2);
+        } else if (e1 instanceof Object[] && e2 instanceof Object[]) {
+            return deepEquals((Object[]) e1, (Object[]) e2);
         } else if (e1 instanceof int[]) {
             return Arrays.equals((int[]) e1, (int[]) e2);
         } else if (e1 instanceof long[]) {
             return Arrays.equals((long[]) e1, (long[]) e2);
+        } else if (e1 instanceof byte[]) {
+            return Arrays.equals((byte[]) e1, (byte[]) e2);
         } else if (e1 instanceof char[]) {
             return Arrays.equals((char[]) e1, (char[]) e2);
+        } else if (e1 instanceof short[]) {
+            return Arrays.equals((short[]) e1, (short[]) e2);
         } else if (e1 instanceof float[]) {
             return Arrays.equals((float[]) e1, (float[]) e2);
         } else if (e1 instanceof double[]) {
@@ -627,6 +628,20 @@
         }
     }
 
+    private static boolean deepEquals(Object[] a1, Object[] a2) {
+        int length = a1.length;
+        if (a2.length != length) {
+            return false;
+        }
+
+        for (int i = 0; i < length; i++) {
+            if (!deepEquals0(a1[i], a2[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public boolean dataEquals(Node a, Node b) {
         assert a.getClass() == b.getClass();
         for (int i = 0; i < data.getCount(); ++i) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeStack.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeStack.java	Thu Apr 06 14:31:32 2017 -0700
@@ -23,14 +23,17 @@
 package org.graalvm.compiler.graph;
 
 public final class NodeStack {
-
-    private static final int INITIAL_SIZE = 8;
+    private static final int DEFAULT_INITIAL_SIZE = 8;
 
     protected Node[] values;
     public int tos;
 
     public NodeStack() {
-        values = new Node[INITIAL_SIZE];
+        this(DEFAULT_INITIAL_SIZE);
+    }
+
+    public NodeStack(int initialSize) {
+        values = new Node[initialSize];
     }
 
     public int size() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java	Thu Apr 06 14:31:32 2017 -0700
@@ -27,7 +27,7 @@
 import java.util.NoSuchElementException;
 import java.util.Queue;
 
-import org.graalvm.compiler.core.common.PermanentBailoutException;
+import org.graalvm.compiler.debug.Debug;
 
 public abstract class NodeWorkList implements Iterable<Node> {
 
@@ -72,26 +72,19 @@
     }
 
     public static final class IterativeNodeWorkList extends NodeWorkList {
-        private static final int HARD_ITERATION_LIMIT = 1_000_000;
         private static final int EXPLICIT_BITMAP_THRESHOLD = 10;
         protected NodeBitMap inQueue;
 
         private int iterationLimit;
-        private boolean hardLimit;
         private Node firstNoChange;
         private Node lastPull;
         private Node lastChain;
 
         public IterativeNodeWorkList(Graph graph, boolean fill, int iterationLimitPerNode) {
             super(graph, fill);
-            if (iterationLimitPerNode > 0) {
-                long limit = (long) iterationLimitPerNode * graph.getNodeCount();
-                iterationLimit = (int) Long.min(Integer.MAX_VALUE, limit);
-                hardLimit = false;
-            } else {
-                iterationLimit = HARD_ITERATION_LIMIT;
-                hardLimit = true;
-            }
+            assert iterationLimitPerNode > 0;
+            long limit = (long) iterationLimitPerNode * graph.getNodeCount();
+            iterationLimit = (int) Long.min(Integer.MAX_VALUE, limit);
         }
 
         @Override
@@ -101,11 +94,8 @@
                 public boolean hasNext() {
                     dropDeleted();
                     if (iterationLimit <= 0) {
-                        if (hardLimit) {
-                            throw new PermanentBailoutException("Iteration limit reached");
-                        } else {
-                            return false;
-                        }
+                        Debug.log(Debug.INFO_LEVEL, "Exceeded iteration limit in IterativeNodeWorkList");
+                        return false;
                     }
                     return !worklist.isEmpty();
                 }
@@ -152,7 +142,7 @@
                         }
                     }
                 }
-                assert checkInfiniteWork(node) : "Readded " + node;
+                assert checkInfiniteWork(node) : "Re-added " + node;
                 if (inQueue != null) {
                     inQueue.markAndGrow(node);
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicate.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicate.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,13 +22,20 @@
  */
 package org.graalvm.compiler.graph.iterators;
 
+import java.util.function.Predicate;
+
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodePredicates.AndPredicate;
 
-public interface NodePredicate {
+public interface NodePredicate extends Predicate<Node> {
 
     boolean apply(Node n);
 
+    @Override
+    default boolean test(Node n) {
+        return apply(n);
+    }
+
     default NodePredicate and(NodePredicate np) {
         return new AndPredicate(this, np);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicates.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/iterators/NodePredicates.java	Thu Apr 06 14:31:32 2017 -0700
@@ -105,6 +105,7 @@
             return !a.apply(n);
         }
 
+        @Override
         public NodePredicate negate() {
             return a;
         }
@@ -148,6 +149,7 @@
             return this;
         }
 
+        @Override
         public NodePredicate negate() {
             return new NegativeTypePredicate(this);
         }
@@ -183,6 +185,7 @@
             return this;
         }
 
+        @Override
         public NodePredicate negate() {
             return new PositiveTypePredicate(this);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Thu Apr 06 14:31:32 2017 -0700
@@ -557,7 +557,7 @@
             LIR lir = getResult().getLIR();
             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(lir.getControlFlowGraph().getStartBlock());
             instructions.add(1, op);
-            Debug.dump(Debug.INFO_LOG_LEVEL, lir, "created rescue dummy op");
+            Debug.dump(Debug.INFO_LEVEL, lir, "created rescue dummy op");
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ClassSubstitutionsTests.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ClassSubstitutionsTests.java	Thu Apr 06 14:31:32 2017 -0700
@@ -48,7 +48,7 @@
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             compile(graph.method(), graph);
             assertNotInGraph(graph, Invoke.class);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
+            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorldTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorldTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -47,7 +47,7 @@
         System.setProperty(CompileTheWorld.LIMITMODS_PROPERTY_NAME, "java.base");
         OptionValues initialOptions = getInitialOptions();
         EconomicMap<OptionKey<?>, Object> compilationOptions = CompileTheWorld.parseOptions("Inline=false");
-        new CompileTheWorld(runtime, (HotSpotGraalCompiler) runtime.getCompiler(), CompileTheWorld.SUN_BOOT_CLASS_PATH, 1, 5, null, null, true, initialOptions, compilationOptions).compile();
+        new CompileTheWorld(runtime, (HotSpotGraalCompiler) runtime.getCompiler(), CompileTheWorld.SUN_BOOT_CLASS_PATH, 1, 5, null, null, false, initialOptions, compilationOptions).compile();
         assert ExitVMOnException.getValue(initialOptions) == originalSetting;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Thu Apr 06 14:31:32 2017 -0700
@@ -57,7 +57,7 @@
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             compile(graph.method(), graph);
             assertNotInGraph(graph, Invoke.class);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
+            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
             throw Debug.handle(e);
@@ -117,59 +117,32 @@
         }
     }
 
-    /**
-     * Disables these tests until we know how to dynamically export the {@code jdk.internal.reflect}
-     * package from the {@code java.base} module to the unnamed module associated with
-     * {@link AsmLoader}. Without such an export, the test fails as follows:
-     *
-     * <pre>
-     * Caused by: java.lang.IllegalAccessError: class org.graalvm.compiler.hotspot.test.ConstantPoolTest
-     * (in unnamed module @0x57599b23) cannot access class jdk.internal.reflect.ConstantPool (in
-     * module java.base) because module java.base does not export jdk.internal.reflect to unnamed
-     * module @0x57599b23
-     * </pre>
-     */
-    private static void assumeJDK8() {
-        // Assume.assumeTrue(Java8OrEarlier);
-    }
-
     @Test
     public void testGetSize() {
-        assumeJDK8();
         Object cp = getConstantPoolForObject();
         test("getSize", cp);
     }
 
     @Test
     public void testGetIntAt() {
-        assumeJDK8();
         test("getIntAt");
     }
 
     @Test
     public void testGetLongAt() {
-        assumeJDK8();
         test("getLongAt");
     }
 
     @Test
     public void testGetFloatAt() {
-        assumeJDK8();
         test("getFloatAt");
     }
 
     @Test
     public void testGetDoubleAt() {
-        assumeJDK8();
         test("getDoubleAt");
     }
 
-    // @Test
-    public void testGetUTF8At() {
-        assumeJDK8();
-        test("getUTF8At");
-    }
-
     private static final String PACKAGE_NAME = ConstantPoolSubstitutionsTests.class.getPackage().getName();
     private static final String PACKAGE_NAME_INTERNAL = PACKAGE_NAME.replace('.', '/');
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/JVMCIInfopointErrorTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/JVMCIInfopointErrorTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -53,6 +53,7 @@
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
 import jdk.vm.ci.code.BytecodeFrame;
+import jdk.vm.ci.code.CodeCacheProvider;
 import jdk.vm.ci.code.VirtualObject;
 import jdk.vm.ci.code.site.InfopointReason;
 import jdk.vm.ci.common.JVMCIError;
@@ -140,8 +141,9 @@
         graph.addAfterFixed(graph.start(), test);
 
         CompilationResult compResult = compile(method, graph);
-        HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(method, null, compResult);
-        getCodeCache().addCode(method, compiledCode, null, null);
+        CodeCacheProvider codeCache = getCodeCache();
+        HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, method, null, compResult);
+        codeCache.addCode(method, compiledCode, null, null);
     }
 
     @Test(expected = JVMCIError.class)
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/LoadJavaMirrorWithKlassTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/LoadJavaMirrorWithKlassTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -58,7 +58,11 @@
     @Override
     @SuppressWarnings("try")
     protected Suites createSuites(OptionValues options) {
-        return super.createSuites(new OptionValues(options, GraalOptions.ImmutableCode, true));
+        return super.createSuites(getOptions());
+    }
+
+    private static OptionValues getOptions() {
+        return new OptionValues(getInitialOptions(), GraalOptions.ImmutableCode, true);
     }
 
     @Override
@@ -77,7 +81,7 @@
 
     @Test
     public void testClassConstant() {
-        test("classConstant");
+        test(getOptions(), "classConstant");
     }
 
     public static Class<?> primitiveClassConstant() {
@@ -86,7 +90,7 @@
 
     @Test
     public void testPrimitiveClassConstant() {
-        test("primitiveClassConstant");
+        test(getOptions(), "primitiveClassConstant");
     }
 
     public static Wrapper compressedClassConstant(Wrapper w) {
@@ -97,7 +101,7 @@
     @Test
     public void testCompressedClassConstant() {
         ArgSupplier arg = () -> new Wrapper();
-        test("compressedClassConstant", arg);
+        test(getOptions(), "compressedClassConstant", arg);
     }
 
     public static Wrapper compressedPrimitiveClassConstant(Wrapper w) {
@@ -108,6 +112,6 @@
     @Test
     public void testCompressedPrimitiveClassConstant() {
         ArgSupplier arg = () -> new Wrapper();
-        test("compressedPrimitiveClassConstant", arg);
+        test(getOptions(), "compressedPrimitiveClassConstant", arg);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013, 2016, 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 org.graalvm.compiler.hotspot.test;
+
+import static org.graalvm.compiler.test.SubprocessUtil.formatExecutedCommand;
+import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
+import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.hotspot.CompilationTask;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link CompilationTask} support for dumping graphs and other info useful for debugging a
+ * compiler crash.
+ */
+public class RetryableCompilationTest extends GraalCompilerTest {
+    @Test
+    public void test() throws IOException {
+        List<String> args = withoutDebuggerArguments(getVMCommandLine());
+
+        args.add("-XX:+BootstrapJVMCI");
+        args.add("-XX:+UseJVMCICompiler");
+        args.add("-Dgraal.CrashAt=Object.*,String.*");
+        args.add("-version");
+
+        ProcessBuilder processBuilder = new ProcessBuilder(args);
+        processBuilder.redirectErrorStream(true);
+        Process process = processBuilder.start();
+        BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+        String forcedCrashString = "Forced crash after compiling";
+        String diagnosticOutputFilePrefix = "Graal diagnostic output saved in ";
+
+        boolean seenForcedCrashString = false;
+        String diagnosticOutputZip = null;
+
+        List<String> outputLines = new ArrayList<>();
+
+        String line;
+        while ((line = stdout.readLine()) != null) {
+            outputLines.add(line);
+            if (line.contains(forcedCrashString)) {
+                seenForcedCrashString = true;
+            } else if (diagnosticOutputZip == null) {
+                int index = line.indexOf(diagnosticOutputFilePrefix);
+                if (index != -1) {
+                    diagnosticOutputZip = line.substring(diagnosticOutputFilePrefix.length()).trim();
+                }
+            }
+        }
+        String dashes = "-------------------------------------------------------";
+        if (!seenForcedCrashString) {
+            Assert.fail(String.format("Did not find '%s' in output of command:%n%s", forcedCrashString, formatExecutedCommand(args, outputLines, dashes, dashes)));
+        }
+        if (diagnosticOutputZip == null) {
+            Assert.fail(String.format("Did not find '%s' in output of command:%n%s", diagnosticOutputFilePrefix, formatExecutedCommand(args, outputLines, dashes, dashes)));
+        }
+
+        File zip = new File(diagnosticOutputZip).getAbsoluteFile();
+        Assert.assertTrue(zip.toString(), zip.exists());
+        try {
+            int bgv = 0;
+            int cfg = 0;
+            ZipFile dd = new ZipFile(diagnosticOutputZip);
+            List<String> entries = new ArrayList<>();
+            for (Enumeration<? extends ZipEntry> e = dd.entries(); e.hasMoreElements();) {
+                ZipEntry ze = e.nextElement();
+                String name = ze.getName();
+                entries.add(name);
+                if (name.endsWith(".bgv")) {
+                    bgv++;
+                } else if (name.endsWith(".cfg")) {
+                    cfg++;
+                }
+            }
+            if (bgv == 0) {
+                Assert.fail(String.format("Expected at least one .bgv file in %s: %s", diagnosticOutputZip, entries));
+            }
+            if (cfg == 0) {
+                Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries));
+            }
+        } finally {
+            zip.delete();
+        }
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -266,7 +266,7 @@
             new GuardLoweringPhase().apply(graph, midContext);
             new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
             new WriteBarrierAdditionPhase(config).apply(graph);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "After Write Barrier Addition");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "After Write Barrier Addition");
 
             int barriers = 0;
             if (config.useG1GC) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Thu Apr 06 14:31:32 2017 -0700
@@ -30,7 +30,20 @@
 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintFilter;
 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintStackTraceOnException;
 import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
+import static org.graalvm.compiler.debug.Debug.INFO_LEVEL;
+import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.DUMP_METHOD;
+import static org.graalvm.compiler.debug.DelegatingDebugConfig.Level.DUMP;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Dump;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DumpPath;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.ForceDebugEnable;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintCFGFileName;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintGraphFile;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintGraphFileName;
 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import org.graalvm.compiler.code.CompilationResult;
@@ -38,7 +51,9 @@
 import org.graalvm.compiler.debug.Debug.Scope;
 import org.graalvm.compiler.debug.DebugCloseable;
 import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.DebugDumpHandler;
 import org.graalvm.compiler.debug.DebugDumpScope;
+import org.graalvm.compiler.debug.DebugRetryableTask;
 import org.graalvm.compiler.debug.DebugTimer;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Management;
@@ -46,6 +61,7 @@
 import org.graalvm.compiler.debug.TimeSource;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugConfigCustomizer;
 import org.graalvm.util.EconomicMap;
 
 import jdk.vm.ci.code.BailoutException;
@@ -96,6 +112,136 @@
     private final boolean useProfilingInfo;
     private final OptionValues options;
 
+    final class RetryableCompilation extends DebugRetryableTask<HotSpotCompilationRequestResult> {
+        private final EventProvider.CompilationEvent compilationEvent;
+        CompilationResult result;
+
+        RetryableCompilation(EventProvider.CompilationEvent compilationEvent) {
+            this.compilationEvent = compilationEvent;
+        }
+
+        @SuppressWarnings("try")
+        @Override
+        protected HotSpotCompilationRequestResult run(Throwable retryCause) {
+            HotSpotResolvedJavaMethod method = getMethod();
+            int entryBCI = getEntryBCI();
+            final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
+            CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR);
+            final boolean printCompilation = PrintCompilation.getValue(options) && !TTY.isSuppressed();
+            final boolean printAfterCompilation = PrintAfterCompilation.getValue(options) && !TTY.isSuppressed();
+            if (printCompilation) {
+                TTY.println(getMethodDescription() + "...");
+            }
+
+            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(options), method);
+            final long start;
+            final long allocatedBytesBefore;
+            if (printAfterCompilation || printCompilation) {
+                final long threadId = Thread.currentThread().getId();
+                start = TimeSource.getTimeNS();
+                allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L;
+            } else {
+                start = 0L;
+                allocatedBytesBefore = 0L;
+            }
+
+            try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
+                // Begin the compilation event.
+                compilationEvent.begin();
+                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options);
+            } catch (Throwable e) {
+                throw Debug.handle(e);
+            } finally {
+                // End the compilation event.
+                compilationEvent.end();
+
+                filter.remove();
+
+                if (printAfterCompilation || printCompilation) {
+                    final long threadId = Thread.currentThread().getId();
+                    final long stop = TimeSource.getTimeNS();
+                    final long duration = (stop - start) / 1000000;
+                    final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1;
+                    final int bytecodeSize = result != null ? result.getBytecodeSize() : 0;
+                    final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId);
+                    final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024;
+
+                    if (printAfterCompilation) {
+                        TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
+                    } else if (printCompilation) {
+                        TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dB %5dkB", getId(), "", "", "", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
+                    }
+                }
+            }
+
+            if (result != null) {
+                try (DebugCloseable b = CodeInstallationTime.start()) {
+                    installMethod(result);
+                }
+            }
+            stats.finish(method, installedCode);
+            if (result != null) {
+                return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize());
+            }
+            return null;
+        }
+
+        @Override
+        protected boolean onRetry(Throwable t) {
+            if (t instanceof BailoutException) {
+                return false;
+            }
+
+            if (!Debug.isEnabled()) {
+                TTY.printf("Error while processing %s.%nRe-run with -D%s%s=true to capture graph dumps upon a compilation failure.%n", CompilationTask.this,
+                                HotSpotGraalOptionValues.GRAAL_OPTION_PROPERTY_PREFIX, ForceDebugEnable.getName());
+                return false;
+            }
+
+            if (Dump.hasBeenSet(options)) {
+                // If dumping is explicitly enabled, Graal is being debugged
+                // so don't interfere with what the user is expecting to see.
+                return false;
+            }
+
+            String outputDirectory = compiler.getGraalRuntime().getOutputDirectory();
+            if (outputDirectory == null) {
+                return false;
+            }
+            String methodFQN = getMethod().format("%H.%n");
+            File dumpPath = new File(outputDirectory, methodFQN);
+            dumpPath.mkdirs();
+            if (!dumpPath.exists()) {
+                TTY.println("Warning: could not create dump directory " + dumpPath);
+                return false;
+            }
+
+            TTY.println("Retrying " + CompilationTask.this);
+            retryDumpHandlers = new ArrayList<>();
+            retryOptions = new OptionValues(options,
+                            PrintGraphFile, true,
+                            PrintCFGFileName, methodFQN,
+                            PrintGraphFileName, methodFQN,
+                            DumpPath, dumpPath.getPath());
+            override(DUMP, INFO_LEVEL).enable(DUMP_METHOD);
+            new GraalDebugConfigCustomizer().customize(this);
+            return true;
+        }
+
+        private Collection<DebugDumpHandler> retryDumpHandlers;
+        private OptionValues retryOptions;
+
+        @Override
+        public Collection<DebugDumpHandler> dumpHandlers() {
+            return retryDumpHandlers;
+        }
+
+        @Override
+        public OptionValues getOptions() {
+            return retryOptions;
+        }
+    }
+
     static class Lazy {
         /**
          * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the
@@ -196,9 +342,8 @@
     public HotSpotCompilationRequestResult runCompilation() {
         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
-        final long threadId = Thread.currentThread().getId();
         int entryBCI = getEntryBCI();
-        final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
+        boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
         HotSpotResolvedJavaMethod method = getMethod();
 
         // register the compilation id in the method metrics
@@ -220,64 +365,9 @@
             return null;
         }
 
-        CompilationResult result = null;
+        RetryableCompilation compilation = new RetryableCompilation(compilationEvent);
         try (DebugCloseable a = CompilationTime.start()) {
-            CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR);
-            final boolean printCompilation = PrintCompilation.getValue(options) && !TTY.isSuppressed();
-            final boolean printAfterCompilation = PrintAfterCompilation.getValue(options) && !TTY.isSuppressed();
-            if (printCompilation) {
-                TTY.println(getMethodDescription() + "...");
-            }
-
-            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(options), method);
-            final long start;
-            final long allocatedBytesBefore;
-            if (printAfterCompilation || printCompilation) {
-                start = TimeSource.getTimeNS();
-                allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L;
-            } else {
-                start = 0L;
-                allocatedBytesBefore = 0L;
-            }
-
-            try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
-                // Begin the compilation event.
-                compilationEvent.begin();
-                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options);
-            } catch (Throwable e) {
-                throw Debug.handle(e);
-            } finally {
-                // End the compilation event.
-                compilationEvent.end();
-
-                filter.remove();
-
-                if (printAfterCompilation || printCompilation) {
-                    final long stop = TimeSource.getTimeNS();
-                    final long duration = (stop - start) / 1000000;
-                    final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1;
-                    final int bytecodeSize = result != null ? result.getBytecodeSize() : 0;
-                    final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId);
-                    final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024;
-
-                    if (printAfterCompilation) {
-                        TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
-                    } else if (printCompilation) {
-                        TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dB %5dkB", getId(), "", "", "", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
-                    }
-                }
-            }
-
-            if (result != null) {
-                try (DebugCloseable b = CodeInstallationTime.start()) {
-                    installMethod(result);
-                }
-            }
-            stats.finish(method, installedCode);
-            if (result != null) {
-                return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize());
-            }
-            return null;
+            return compilation.execute();
         } catch (BailoutException bailout) {
             BAILOUTS.increment();
             if (ExitVMOnBailout.getValue(options)) {
@@ -319,8 +409,9 @@
             try {
                 int compiledBytecodes = 0;
                 int codeSize = 0;
-                if (result != null) {
-                    compiledBytecodes = result.getBytecodeSize();
+
+                if (compilation.result != null) {
+                    compiledBytecodes = compilation.result.getBytecodeSize();
                     CompiledBytecodes.add(compiledBytecodes);
                     if (installedCode != null) {
                         codeSize = installedCode.getSize();
@@ -334,7 +425,7 @@
                     compilationEvent.setMethod(method.format("%H.%n(%p)"));
                     compilationEvent.setCompileId(getId());
                     compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
-                    compilationEvent.setSucceeded(result != null && installedCode != null);
+                    compilationEvent.setSucceeded(compilation.result != null && installedCode != null);
                     compilationEvent.setIsOsr(isOSR);
                     compilationEvent.setCodeSize(codeSize);
                     compilationEvent.setInlinedBytes(compiledBytecodes);
@@ -387,7 +478,7 @@
         installedCode = null;
         Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
         try (Scope s = Debug.scope("CodeInstall", context)) {
-            HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(getRequest().getMethod(), getRequest(), compResult);
+            HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, getRequest().getMethod(), getRequest(), compResult);
             installedCode = (HotSpotInstalledCode) codeCache.installCode(getRequest().getMethod(), compiledCode, null, getRequest().getMethod().getSpeculationLog(), installAsDefault);
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java	Thu Apr 06 14:31:32 2017 -0700
@@ -267,7 +267,31 @@
     public final int secondarySuperCacheOffset = getFieldOffset("Klass::_secondary_super_cache", Integer.class, "Klass*");
     public final int secondarySupersOffset = getFieldOffset("Klass::_secondary_supers", Integer.class, "Array<Klass*>*");
 
-    public final int classMirrorOffset = getFieldOffset("Klass::_java_mirror", Integer.class, "oop");
+    public final boolean classMirrorIsHandle;
+    public final int classMirrorOffset;
+    {
+        String name = "Klass::_java_mirror";
+        int offset = -1;
+        boolean isHandle = false;
+        try {
+            offset = getFieldOffset(name, Integer.class, "oop");
+        } catch (JVMCIError e) {
+
+        }
+        if (offset == -1) {
+            try {
+                offset = getFieldOffset(name, Integer.class, "jobject");
+                isHandle = true;
+            } catch (JVMCIError e) {
+
+            }
+        }
+        if (offset == -1) {
+            throw new JVMCIError("cannot get offset of field " + name + " with type oop or jobject");
+        }
+        classMirrorOffset = offset;
+        classMirrorIsHandle = isHandle;
+    }
 
     public final int klassSuperKlassOffset = getFieldOffset("Klass::_super", Integer.class, "Klass*");
     public final int klassModifierFlagsOffset = getFieldOffset("Klass::_modifier_flags", Integer.class, "jint");
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java	Thu Apr 06 14:31:32 2017 -0700
@@ -406,7 +406,7 @@
     @Override
     public CompiledCode createCompiledCode(ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compResult) {
         HotSpotCompilationRequest compRequest = compilationRequest instanceof HotSpotCompilationRequest ? (HotSpotCompilationRequest) compilationRequest : null;
-        return HotSpotCompiledCodeBuilder.createCompiledCode(method, compRequest, compResult);
+        return HotSpotCompiledCodeBuilder.createCompiledCode(getCodeCache(), method, compRequest, compResult);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotCompiledCodeBuilder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotCompiledCodeBuilder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -42,6 +42,7 @@
 import org.graalvm.compiler.code.SourceMapping;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 
+import jdk.vm.ci.code.CodeCacheProvider;
 import jdk.vm.ci.code.DebugInfo;
 import jdk.vm.ci.code.StackSlot;
 import jdk.vm.ci.code.site.ConstantReference;
@@ -61,13 +62,13 @@
 
 public class HotSpotCompiledCodeBuilder {
 
-    public static HotSpotCompiledCode createCompiledCode(ResolvedJavaMethod method, HotSpotCompilationRequest compRequest, CompilationResult compResult) {
+    public static HotSpotCompiledCode createCompiledCode(CodeCacheProvider codeCache, ResolvedJavaMethod method, HotSpotCompilationRequest compRequest, CompilationResult compResult) {
         String name = compResult.getName();
 
         byte[] targetCode = compResult.getTargetCode();
         int targetCodeSize = compResult.getTargetCodeSize();
 
-        Site[] sites = getSortedSites(compResult);
+        Site[] sites = getSortedSites(codeCache, compResult);
 
         Assumption[] assumptions = compResult.getAssumptions();
 
@@ -201,7 +202,7 @@
      * {@code DebugInformationRecorder::add_new_pc_offset}). In addition, it expects
      * {@link Infopoint} PCs to be unique.
      */
-    private static Site[] getSortedSites(CompilationResult target) {
+    private static Site[] getSortedSites(CodeCacheProvider codeCache, CompilationResult target) {
         List<Site> sites = new ArrayList<>(
                         target.getExceptionHandlers().size() + target.getInfopoints().size() + target.getDataPatches().size() + target.getMarks().size() + target.getSourceMappings().size());
         sites.addAll(target.getExceptionHandlers());
@@ -214,9 +215,11 @@
          * can really be represented and recording the end PC seems to give the best results and
          * corresponds with what C1 and C2 do.
          */
-        for (SourceMapping source : target.getSourceMappings()) {
-            sites.add(new Infopoint(source.getEndOffset(), new DebugInfo(source.getSourcePosition()), InfopointReason.BYTECODE_POSITION));
-            assert verifySourcePositionReceivers(source.getSourcePosition());
+        if (codeCache.shouldDebugNonSafepoints()) {
+            for (SourceMapping source : target.getSourceMappings()) {
+                sites.add(new Infopoint(source.getEndOffset(), new DebugInfo(source.getSourcePosition()), InfopointReason.BYTECODE_POSITION));
+                assert verifySourcePositionReceivers(source.getSourcePosition());
+            }
         }
 
         SiteComparator c = new SiteComparator();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -71,8 +71,8 @@
         VirtualStackSlot slot = lockStack.makeLockSlot(lockDepth);
         ValueNode lock = state.lockAt(lockIndex);
         JavaValue object = toJavaValue(lock);
-        boolean eliminated = object instanceof VirtualObject || state.monitorIdAt(lockIndex) == null;
-        assert state.monitorIdAt(lockIndex) == null || state.monitorIdAt(lockIndex).getLockDepth() == lockDepth;
+        boolean eliminated = object instanceof VirtualObject || state.monitorIdAt(lockIndex).isEliminated();
+        assert state.monitorIdAt(lockIndex).getLockDepth() == lockDepth;
         return new StackLockValue(object, slot, eliminated);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java	Thu Apr 06 14:31:32 2017 -0700
@@ -24,6 +24,7 @@
 
 import static org.graalvm.compiler.core.common.GraalOptions.OptAssumptions;
 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
+
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.util.Formattable;
@@ -98,6 +99,9 @@
     @Override
     @SuppressWarnings("try")
     public CompilationRequestResult compileMethod(CompilationRequest request) {
+        if (graalRuntime.isShutdown()) {
+            return HotSpotCompilationRequestResult.failure(String.format("Shutdown entered"), false);
+        }
         OptionValues options = graalRuntime.getOptions();
         if (graalRuntime.isBootstrapping()) {
             if (GraalDebugConfig.Options.BootstrapInitializeOnly.getValue(options)) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java	Thu Apr 06 14:31:32 2017 -0700
@@ -45,6 +45,7 @@
 public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFactory {
 
     private static MethodFilter[] graalCompileOnlyFilter;
+    private static boolean compileGraalWithC1Only;
 
     private final HotSpotGraalJVMCIServiceLocator locator;
 
@@ -67,18 +68,17 @@
         JVMCIVersionCheck.check(false);
         assert options == null : "cannot select " + getClass() + " service more than once";
         options = HotSpotGraalOptionValues.HOTSPOT_OPTIONS;
-        initializeGraalCompileOnlyFilter(options);
-        if (graalCompileOnlyFilter != null || !Options.UseTrivialPrefixes.getValue(options)) {
-            /*
-             * Exercise this code path early to encourage loading now. This doesn't solve problem of
-             * deadlock during class loading but seems to eliminate it in practice.
-             */
-            adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.FullOptimization);
-            adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.Simple);
-        }
+        initializeGraalCompilePolicyFields(options);
+        /*
+         * Exercise this code path early to encourage loading now. This doesn't solve problem of
+         * deadlock during class loading but seems to eliminate it in practice.
+         */
+        adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.FullOptimization);
+        adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.Simple);
     }
 
-    private static void initializeGraalCompileOnlyFilter(OptionValues options) {
+    private static void initializeGraalCompilePolicyFields(OptionValues options) {
+        compileGraalWithC1Only = Options.CompileGraalWithC1Only.getValue(options);
         String optionValue = Options.GraalCompileOnly.getValue(options);
         if (optionValue != null) {
             MethodFilter[] filter = MethodFilter.parse(optionValue);
@@ -101,9 +101,6 @@
         @Option(help = "In tiered mode compile Graal and JVMCI using optimized first tier code.", type = OptionType.Expert)
         public static final OptionKey<Boolean> CompileGraalWithC1Only = new OptionKey<>(true);
 
-        @Option(help = "Hook into VM-level mechanism for denoting compilations to be performed in first tier.", type = OptionType.Expert)
-        public static final OptionKey<Boolean> UseTrivialPrefixes = new OptionKey<>(false);
-
         @Option(help = "A method filter selecting what should be compiled by Graal.  All other requests will be reduced to CompilationLevel.Simple.", type = OptionType.Expert)
         public static final OptionKey<String> GraalCompileOnly = new OptionKey<>(null);
         // @formatter:on
@@ -137,27 +134,15 @@
     }
 
     @Override
-    public String[] getTrivialPrefixes() {
-        if (Options.UseTrivialPrefixes.getValue(options)) {
-            if (Options.CompileGraalWithC1Only.getValue(options)) {
-                return new String[]{"jdk/vm/ci", "org/graalvm/compiler", "com/oracle/graal"};
-            }
-        }
-        return null;
-    }
-
-    @Override
     public CompilationLevelAdjustment getCompilationLevelAdjustment() {
         if (graalCompileOnlyFilter != null) {
             return CompilationLevelAdjustment.ByFullSignature;
         }
-        if (!Options.UseTrivialPrefixes.getValue(options)) {
-            if (Options.CompileGraalWithC1Only.getValue(options)) {
-                // We only decide using the class declaring the method
-                // so no need to have the method name and signature
-                // symbols converted to a String.
-                return CompilationLevelAdjustment.ByHolder;
-            }
+        if (compileGraalWithC1Only) {
+            // We only decide using the class declaring the method
+            // so no need to have the method name and signature
+            // symbols converted to a String.
+            return CompilationLevelAdjustment.ByHolder;
         }
         return CompilationLevelAdjustment.None;
     }
@@ -193,10 +178,12 @@
                 return CompilationLevel.Simple;
             }
         }
-        if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
-            String declaringClassName = declaringClass.getName();
-            if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm.compiler") || declaringClassName.startsWith("com.oracle.graal")) {
-                return CompilationLevel.Simple;
+        if (compileGraalWithC1Only) {
+            if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
+                String declaringClassName = declaringClass.getName();
+                if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm") || declaringClassName.startsWith("com.oracle.graal")) {
+                    return CompilationLevel.Simple;
+                }
             }
         }
         return level;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalJVMCIServiceLocator.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalJVMCIServiceLocator.java	Thu Apr 06 14:31:32 2017 -0700
@@ -30,68 +30,77 @@
 import org.graalvm.compiler.serviceprovider.ServiceProvider;
 
 import jdk.vm.ci.hotspot.HotSpotVMEventListener;
-import jdk.vm.ci.runtime.JVMCICompiler;
 import jdk.vm.ci.runtime.JVMCICompilerFactory;
 import jdk.vm.ci.services.JVMCIServiceLocator;
+import jdk.vm.ci.services.Services;
 
 @ServiceProvider(JVMCIServiceLocator.class)
 public final class HotSpotGraalJVMCIServiceLocator extends JVMCIServiceLocator {
 
-    private boolean exportsAdded;
-
     /**
-     * Dynamically exports and opens various internal JDK packages to the Graal module. This
-     * requires only a single {@code --add-exports=java.base/jdk.internal.module=<Graal module>} on
-     * the VM command line instead of a {@code --add-exports} instance for each JDK internal package
-     * used by Graal.
+     * Holds the state shared between all {@link HotSpotGraalJVMCIServiceLocator} instances. This is
+     * necessary as {@link Services} can create a new instance of a service provider each time
+     * {@link Services#load(Class)} or {@link Services#loadSingle(Class, boolean)} is called.
      */
-    private void addExports() {
-        if (JAVA_SPECIFICATION_VERSION >= 9 && !exportsAdded) {
-            Object javaBaseModule = getModule.invoke(String.class);
-            Object graalModule = getModule.invoke(getClass());
-            addExports.invokeStatic(javaBaseModule, "jdk.internal.misc", graalModule);
-            addExports.invokeStatic(javaBaseModule, "jdk.internal.jimage", graalModule);
-            addExports.invokeStatic(javaBaseModule, "com.sun.crypto.provider", graalModule);
-            addOpens.invokeStatic(javaBaseModule, "jdk.internal.misc", graalModule);
-            addOpens.invokeStatic(javaBaseModule, "jdk.internal.jimage", graalModule);
-            addOpens.invokeStatic(javaBaseModule, "com.sun.crypto.provider", graalModule);
-            exportsAdded = true;
+    private static final class Shared {
+        static final Shared SINGLETON = new Shared();
+
+        private boolean exportsAdded;
+
+        /**
+         * Dynamically exports and opens various internal JDK packages to the Graal module. This
+         * requires only a single {@code --add-exports=java.base/jdk.internal.module=<Graal module>}
+         * on the VM command line instead of a {@code --add-exports} instance for each JDK internal
+         * package used by Graal.
+         */
+        private void addExports() {
+            if (JAVA_SPECIFICATION_VERSION >= 9 && !exportsAdded) {
+                Object javaBaseModule = getModule.invoke(String.class);
+                Object graalModule = getModule.invoke(getClass());
+                addExports.invokeStatic(javaBaseModule, "jdk.internal.misc", graalModule);
+                addExports.invokeStatic(javaBaseModule, "jdk.internal.jimage", graalModule);
+                addExports.invokeStatic(javaBaseModule, "com.sun.crypto.provider", graalModule);
+                addOpens.invokeStatic(javaBaseModule, "jdk.internal.misc", graalModule);
+                addOpens.invokeStatic(javaBaseModule, "jdk.internal.jimage", graalModule);
+                addOpens.invokeStatic(javaBaseModule, "com.sun.crypto.provider", graalModule);
+                exportsAdded = true;
+            }
+        }
+
+        <T> T getProvider(Class<T> service, HotSpotGraalJVMCIServiceLocator locator) {
+            if (service == JVMCICompilerFactory.class) {
+                addExports();
+                return service.cast(new HotSpotGraalCompilerFactory(locator));
+            } else if (service == HotSpotVMEventListener.class) {
+                if (graalRuntime != null) {
+                    addExports();
+                    return service.cast(new HotSpotGraalVMEventListener(graalRuntime));
+                }
+            }
+            return null;
+        }
+
+        private HotSpotGraalRuntime graalRuntime;
+
+        /**
+         * Notifies this object of the compiler created via {@link HotSpotGraalJVMCIServiceLocator}.
+         */
+        void onCompilerCreation(HotSpotGraalCompiler compiler) {
+            assert this.graalRuntime == null : "only expect a single JVMCICompiler to be created";
+            this.graalRuntime = (HotSpotGraalRuntime) compiler.getGraalRuntime();
         }
     }
 
-    private HotSpotGraalRuntime graalRuntime;
-
     @Override
     public <T> T getProvider(Class<T> service) {
-        if (service == JVMCICompilerFactory.class) {
-            addExports();
-            return service.cast(new HotSpotGraalCompilerFactory(this));
-        } else if (service == HotSpotVMEventListener.class) {
-            if (graalRuntime != null) {
-                addExports();
-                return service.cast(new HotSpotGraalVMEventListener(graalRuntime));
-            }
-        }
-        return null;
+        return Shared.SINGLETON.getProvider(service, this);
     }
 
     /**
-     * The signature cannot mention HotSpotGraalCompiler since it indirectly references
-     * JVMCICompiler which is in a non-exported JVMCI package. This causes an IllegalAccessError
-     * while looking for the
-     * <a href="http://hg.openjdk.java.net/jdk9/hs/jdk/rev/89ef4b822745#l32.65">provider</a> factory
-     * method:
-     *
-     * <pre>
-     * java.util.ServiceConfigurationError: jdk.vm.ci.services.JVMCIServiceLocator: Unable to get public provider() method
-     * ...
-     * Caused by: java.lang.IllegalAccessError: superinterface check failed: class org.graalvm.compiler.api.runtime.GraalJVMCICompiler
-     * (in module org.graalvm.compiler.graal_core) cannot access class jdk.vm.ci.runtime.JVMCICompiler (in module jdk.vm.ci) because
-     * module jdk.vm.ci does not export jdk.vm.ci.runtime to module org.graalvm.compiler.graal_core
-     * </pre>
+     * Notifies this object of the compiler created via {@link HotSpotGraalJVMCIServiceLocator}.
      */
-    void onCompilerCreation(JVMCICompiler compiler) {
-        assert this.graalRuntime == null : "only expect a single JVMCICompiler to be created";
-        this.graalRuntime = (HotSpotGraalRuntime) ((HotSpotGraalCompiler) compiler).getGraalRuntime();
+    @SuppressWarnings("static-method")
+    void onCompilerCreation(HotSpotGraalCompiler compiler) {
+        Shared.SINGLETON.onCompilerCreation(compiler);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java	Thu Apr 06 14:31:32 2017 -0700
@@ -34,8 +34,22 @@
 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.MethodFilter;
 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Verify;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.api.runtime.GraalRuntime;
@@ -278,6 +292,7 @@
     }
 
     private long runtimeStartTime;
+    private boolean shutdown;
 
     /**
      * Take action related to entering a new execution phase.
@@ -291,6 +306,7 @@
     }
 
     void shutdown() {
+        shutdown = true;
         if (debugValuesPrinter != null) {
             debugValuesPrinter.printDebugValues(options);
         }
@@ -302,6 +318,8 @@
             }
         }
         BenchmarkCounters.shutdown(runtime(), options, runtimeStartTime);
+
+        archiveAndDeleteOutputDirectory();
     }
 
     void clearMeters() {
@@ -321,4 +339,95 @@
     public boolean isBootstrapping() {
         return bootstrapJVMCI && !bootstrapFinished;
     }
+
+    @Override
+    public boolean isShutdown() {
+        return shutdown;
+    }
+
+    /**
+     * Gets a unique identifier for this execution such as a process ID.
+     */
+    private static String getExecutionID() {
+        String runtimeName = ManagementFactory.getRuntimeMXBean().getName();
+        try {
+            int index = runtimeName.indexOf('@');
+            if (index != -1) {
+                long pid = Long.parseLong(runtimeName.substring(0, index));
+                return Long.toString(pid);
+            }
+        } catch (NumberFormatException e) {
+        }
+        return runtimeName;
+    }
+
+    private String outputDirectory;
+
+    @Override
+    public String getOutputDirectory() {
+        if (outputDirectory == null) {
+            outputDirectory = "graal_output_" + getExecutionID();
+            File dir = new File(outputDirectory).getAbsoluteFile();
+            if (!dir.exists()) {
+                dir.mkdirs();
+                if (!dir.exists()) {
+                    TTY.println("Warning: could not create Graal diagnostic directory " + dir);
+                    return null;
+                }
+            }
+        }
+        return outputDirectory;
+    }
+
+    /**
+     * Archives and deletes the {@linkplain #getOutputDirectory() output directory} if it exists.
+     */
+    private void archiveAndDeleteOutputDirectory() {
+        if (outputDirectory != null) {
+            Path dir = Paths.get(outputDirectory);
+            if (dir.toFile().exists()) {
+                try {
+                    // Give compiler threads a chance to finishing dumping
+                    Thread.sleep(1000);
+                } catch (InterruptedException e1) {
+                }
+                File zip = new File(outputDirectory + ".zip").getAbsoluteFile();
+                List<Path> toDelete = new ArrayList<>();
+                try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip))) {
+                    zos.setLevel(Deflater.BEST_COMPRESSION);
+                    Files.walkFileTree(dir, Collections.emptySet(), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
+                        @Override
+                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                            if (attrs.isRegularFile()) {
+                                ZipEntry ze = new ZipEntry(file.toString());
+                                zos.putNextEntry(ze);
+                                zos.write(Files.readAllBytes(file));
+                                zos.closeEntry();
+                            }
+                            toDelete.add(file);
+                            return FileVisitResult.CONTINUE;
+                        }
+
+                        @Override
+                        public FileVisitResult postVisitDirectory(Path d, IOException exc) throws IOException {
+                            toDelete.add(d);
+                            return FileVisitResult.CONTINUE;
+                        }
+                    });
+                    TTY.println("Graal diagnostic output saved in %s", zip);
+                } catch (IOException e) {
+                    TTY.printf("IO error archiving %s:%n", dir);
+                    e.printStackTrace(TTY.out);
+                }
+                for (Path p : toDelete) {
+                    try {
+                        Files.delete(p);
+                    } catch (IOException e) {
+                        TTY.printf("IO error deleting %s:%n", p);
+                        e.printStackTrace(TTY.out);
+                    }
+                }
+            }
+        }
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntimeProvider.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntimeProvider.java	Thu Apr 06 14:31:32 2017 -0700
@@ -62,4 +62,19 @@
      * Determines if the VM is currently bootstrapping the JVMCI compiler.
      */
     boolean isBootstrapping();
+
+    /**
+     * This runtime has been requested to shutdown.
+     */
+    boolean isShutdown();
+
+    /**
+     * Gets a directory into which diagnostics such crash reports and dumps should be written. This
+     * method will create the directory if it doesn't exist so it should only be called if
+     * diagnostics are about to be generated.
+     *
+     * @return the directory into which diagnostics can be written or {@code null} if the directory
+     *         does not exist and could not be created
+     */
+    String getOutputDirectory();
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalVMEventListener.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalVMEventListener.java	Thu Apr 06 14:31:32 2017 -0700
@@ -46,10 +46,10 @@
 
     @Override
     public void notifyInstall(HotSpotCodeCacheProvider codeCache, InstalledCode installedCode, CompiledCode compiledCode) {
-        if (Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
+        if (Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
             CompilationResult compResult = Debug.contextLookup(CompilationResult.class);
             assert compResult != null : "can't dump installed code properly without CompilationResult";
-            Debug.dump(Debug.BASIC_LOG_LEVEL, installedCode, "After code installation");
+            Debug.dump(Debug.BASIC_LEVEL, installedCode, "After code installation");
         }
         if (Debug.isLogEnabled()) {
             Debug.log("%s", codeCache.disassemble(installedCode));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/PrintStreamOptionKey.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/PrintStreamOptionKey.java	Thu Apr 06 14:31:32 2017 -0700
@@ -45,7 +45,7 @@
     }
 
     /**
-     * Replace any instance of %p with a an identifying name. Try to get it from the RuntimeMXBean
+     * Replace any instance of %p with an identifying name. Try to get it from the RuntimeMXBean
      * name.
      *
      * @return the name of the file to log to
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Thu Apr 06 14:31:32 2017 -0700
@@ -31,6 +31,7 @@
 import static org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProviderImpl.OSR_MIGRATION_END;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_KLASS_LOCATION;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_LOCATION;
+import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_HANDLE_LOCATION;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.COMPRESSED_HUB_LOCATION;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.DISPLACED_MARK_WORD_LOCATION;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.HUB_LOCATION;
@@ -40,6 +41,7 @@
 import java.lang.ref.Reference;
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
+import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
@@ -409,10 +411,17 @@
             return;
         }
 
+        ValueNode hub = n.getHub();
+        GraalHotSpotVMConfig vmConfig = runtime.getVMConfig();
         StructuredGraph graph = n.graph();
-        assert !n.getHub().isConstant();
-        AddressNode address = createOffsetAddress(graph, n.getHub(), runtime.getVMConfig().classMirrorOffset);
-        FloatingReadNode read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_LOCATION, null, n.stamp(), null, BarrierType.NONE));
+        assert !hub.isConstant() || GraalOptions.ImmutableCode.getValue(graph.getOptions());
+        AddressNode mirrorAddress = createOffsetAddress(graph, hub, vmConfig.classMirrorOffset);
+        FloatingReadNode read = graph.unique(new FloatingReadNode(mirrorAddress, CLASS_MIRROR_LOCATION, null, vmConfig.classMirrorIsHandle ? StampFactory.forKind(target.wordJavaKind) : n.stamp(),
+                        null, BarrierType.NONE));
+        if (vmConfig.classMirrorIsHandle) {
+            AddressNode address = createOffsetAddress(graph, read, 0);
+            read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_HANDLE_LOCATION, null, n.stamp(), null, BarrierType.NONE));
+        }
         n.replaceAtUsagesAndDelete(read);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Thu Apr 06 14:31:32 2017 -0700
@@ -323,7 +323,8 @@
      */
     private static ValueNode getMetaspaceConstantPool(GraphBuilderContext b, ValueNode constantPoolOop, WordTypes wordTypes, GraalHotSpotVMConfig config) {
         // ConstantPool.constantPoolOop is in fact the holder class.
-        ClassGetHubNode klass = b.add(new ClassGetHubNode(b.nullCheckedValue(constantPoolOop, DeoptimizationAction.None)));
+        ValueNode value = b.nullCheckedValue(constantPoolOop, DeoptimizationAction.None);
+        ValueNode klass = b.add(ClassGetHubNode.create(value, b.getMetaAccess(), b.getConstantReflection(), false));
 
         boolean notCompressible = false;
         AddressNode constantsAddress = b.add(new OffsetAddressNode(klass, b.add(ConstantNode.forLong(config.instanceKlassConstantsOffset))));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Thu Apr 06 14:31:32 2017 -0700
@@ -90,7 +90,7 @@
 
         if (ImmutableCode.getValue(options)) {
             // lowering introduces class constants, therefore it must be after lowering
-            ret.getHighTier().appendPhase(new LoadJavaMirrorWithKlassPhase(config.classMirrorOffset, config.useCompressedOops ? config.getOopEncoding() : null));
+            ret.getHighTier().appendPhase(new LoadJavaMirrorWithKlassPhase(config));
             if (VerifyPhases.getValue(options)) {
                 ret.getHighTier().appendPhase(new AheadOfTimeVerificationPhase());
             }
@@ -146,10 +146,10 @@
             protected void run(StructuredGraph graph, HighTierContext context) {
                 EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, runtime.getTarget().arch);
 
-                SimplifyingGraphDecoder graphDecoder = new SimplifyingGraphDecoder(context.getMetaAccess(), context.getConstantReflection(), context.getConstantFieldProvider(),
-                                context.getStampProvider(), !ImmutableCode.getValue(graph.getOptions()), runtime.getTarget().arch);
                 StructuredGraph targetGraph = new StructuredGraph.Builder(graph.getOptions(), AllowAssumptions.YES).method(graph.method()).build();
-                graphDecoder.decode(targetGraph, encodedGraph);
+                SimplifyingGraphDecoder graphDecoder = new SimplifyingGraphDecoder(runtime.getTarget().arch, targetGraph, context.getMetaAccess(), context.getConstantReflection(),
+                                context.getConstantFieldProvider(), context.getStampProvider(), !ImmutableCode.getValue(graph.getOptions()));
+                graphDecoder.decode(encodedGraph);
             }
 
             @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java	Thu Apr 06 14:31:32 2017 -0700
@@ -109,7 +109,7 @@
                 PointerEqualsNode comparison = b.add(new PointerEqualsNode(left, right));
                 ValueNode eqValue = b.add(forBoolean(opcode == POINTER_EQ));
                 ValueNode neValue = b.add(forBoolean(opcode == POINTER_NE));
-                b.addPush(returnKind, new ConditionalNode(comparison, eqValue, neValue));
+                b.addPush(returnKind, ConditionalNode.create(comparison, eqValue, neValue));
                 break;
 
             case IS_NULL:
@@ -118,7 +118,7 @@
                 assert pointer.stamp() instanceof MetaspacePointerStamp;
 
                 LogicNode isNull = b.add(IsNullNode.create(pointer));
-                b.addPush(returnKind, new ConditionalNode(isNull, b.add(forBoolean(true)), b.add(forBoolean(false))));
+                b.addPush(returnKind, ConditionalNode.create(isNull, b.add(forBoolean(true)), b.add(forBoolean(false))));
                 break;
 
             case FROM_POINTER:
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/GraalHotSpotVMConfigNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/GraalHotSpotVMConfigNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -29,6 +29,7 @@
 
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -72,8 +73,8 @@
      * @param config
      * @param markId id of the config value
      */
-    public GraalHotSpotVMConfigNode(@InjectedNodeParameter GraalHotSpotVMConfig config, int markId) {
-        super(TYPE, StampFactory.forNodeIntrinsic());
+    public GraalHotSpotVMConfigNode(@InjectedNodeParameter Stamp stamp, @InjectedNodeParameter GraalHotSpotVMConfig config, int markId) {
+        super(TYPE, stamp);
         this.config = config;
         this.markId = markId;
     }
@@ -85,7 +86,7 @@
      * @param markId id of the config value
      * @param kind explicit type of the node
      */
-    public GraalHotSpotVMConfigNode(@InjectedNodeParameter GraalHotSpotVMConfig config, int markId, JavaKind kind) {
+    public GraalHotSpotVMConfigNode(GraalHotSpotVMConfig config, int markId, JavaKind kind) {
         super(TYPE, StampFactory.forKind(kind));
         this.config = config;
         this.markId = markId;
@@ -100,13 +101,13 @@
     @NodeIntrinsic
     private static native boolean areConfigValuesConstant();
 
-    @NodeIntrinsic(setStampFromReturnType = true)
+    @NodeIntrinsic
     private static native long loadLongConfigValue(@ConstantNodeParameter int markId);
 
-    @NodeIntrinsic(setStampFromReturnType = true)
+    @NodeIntrinsic
     private static native int loadIntConfigValue(@ConstantNodeParameter int markId);
 
-    @NodeIntrinsic(setStampFromReturnType = true)
+    @NodeIntrinsic
     private static native byte loadByteConfigValue(@ConstantNodeParameter int markId);
 
     public static long cardTableAddress() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/StubForeignCallNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/StubForeignCallNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,7 +32,7 @@
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
-import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil;
@@ -44,7 +44,6 @@
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
-import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.Value;
 
 /**
@@ -59,8 +58,8 @@
 
     protected final ForeignCallDescriptor descriptor;
 
-    public StubForeignCallNode(@InjectedNodeParameter ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, ValueNode... arguments) {
-        super(TYPE, StampFactory.forKind(JavaKind.fromJavaClass(descriptor.getResultType())));
+    public StubForeignCallNode(@InjectedNodeParameter ForeignCallsProvider foreignCalls, @InjectedNodeParameter Stamp stamp, ForeignCallDescriptor descriptor, ValueNode... arguments) {
+        super(TYPE, stamp);
         this.arguments = new NodeInputList<>(this, arguments);
         this.descriptor = descriptor;
         this.foreignCalls = foreignCalls;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/EncodedSymbolNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/EncodedSymbolNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.hotspot.nodes.aot;
 
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -44,8 +45,8 @@
 
     @OptionalInput protected ValueNode value;
 
-    public EncodedSymbolNode(ValueNode value) {
-        super(TYPE, null);
+    public EncodedSymbolNode(@InjectedNodeParameter Stamp stamp, ValueNode value) {
+        super(TYPE, stamp);
         assert value != null;
         this.value = value;
     }
@@ -61,6 +62,6 @@
         return this;
     }
 
-    @NodeIntrinsic(setStampFromReturnType = true)
+    @NodeIntrinsic
     public static native Word encode(Object constant);
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/LoadJavaMirrorWithKlassPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/LoadJavaMirrorWithKlassPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,7 +22,6 @@
  */
 package org.graalvm.compiler.hotspot.phases;
 
-import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_LOCATION;
 import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes;
 import static org.graalvm.compiler.nodes.NamedLocationIdentity.FINAL_LOCATION;
 
@@ -32,9 +31,11 @@
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.nodes.CompressionNode;
 import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
 import org.graalvm.compiler.hotspot.nodes.type.NarrowOopStamp;
+import org.graalvm.compiler.hotspot.replacements.HubGetClassNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -67,12 +68,10 @@
  */
 public class LoadJavaMirrorWithKlassPhase extends BasePhase<PhaseContext> {
 
-    private final int classMirrorOffset;
     private final CompressEncoding oopEncoding;
 
-    public LoadJavaMirrorWithKlassPhase(int classMirrorOffset, CompressEncoding oopEncoding) {
-        this.classMirrorOffset = classMirrorOffset;
-        this.oopEncoding = oopEncoding;
+    public LoadJavaMirrorWithKlassPhase(GraalHotSpotVMConfig config) {
+        this.oopEncoding = config.useCompressedOops ? config.getOopEncoding() : null;
     }
 
     private ValueNode getClassConstantReplacement(StructuredGraph graph, PhaseContext context, JavaConstant constant) {
@@ -85,13 +84,12 @@
 
                 if (type instanceof HotSpotResolvedObjectType) {
                     ConstantNode klass = ConstantNode.forConstant(KlassPointerStamp.klassNonNull(), ((HotSpotResolvedObjectType) type).klass(), metaAccess, graph);
-                    AddressNode address = graph.unique(new OffsetAddressNode(klass, ConstantNode.forLong(classMirrorOffset, graph)));
-                    ValueNode read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_LOCATION, null, stamp));
+                    ValueNode getClass = graph.unique(new HubGetClassNode(metaAccess, klass));
 
                     if (((HotSpotObjectConstant) constant).isCompressed()) {
-                        return CompressionNode.compress(read, oopEncoding);
+                        return CompressionNode.compress(getClass, oopEncoding);
                     } else {
-                        return read;
+                        return getClass;
                     }
                 } else {
                     /*
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -94,7 +94,7 @@
             assert graph.getNodes(EntryMarkerNode.TYPE).isEmpty();
             return;
         }
-        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement initial at bci %d", graph.getEntryBCI());
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement initial at bci %d", graph.getEntryBCI());
 
         EntryMarkerNode osr;
         int maxIterations = -1;
@@ -144,7 +144,7 @@
                 proxy.replaceAndDelete(proxy.value());
             }
             GraphUtil.removeFixedWithUnusedInputs(osr);
-            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement loop peeling result");
+            Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement loop peeling result");
         } while (true);
 
         FrameState osrState = osr.stateAfter();
@@ -157,7 +157,7 @@
         graph.setStart(osrStart);
         osrStart.setStateAfter(osrState);
 
-        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement after setting OSR start");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement after setting OSR start");
         final int localsSize = osrState.localsSize();
         final int locksSize = osrState.locksSize();
 
@@ -188,9 +188,9 @@
         }
 
         osr.replaceAtUsages(InputType.Guard, osrStart);
-        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement after replacing entry proxies");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement after replacing entry proxies");
         GraphUtil.killCFG(start);
-        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement result");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement result");
         new DeadCodeEliminationPhase(Required).apply(graph);
 
         if (currentOSRWithLocks) {
@@ -210,7 +210,7 @@
                 osrMonitorEnter.setNext(oldNext);
                 osrStart.setNext(osrMonitorEnter);
             }
-            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "After inserting OSR monitor enters");
+            Debug.dump(Debug.DETAILED_LEVEL, graph, "After inserting OSR monitor enters");
             /*
              * Ensure balanced monitorenter - monitorexit
              *
@@ -226,7 +226,7 @@
                 }
             }
         }
-        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement result");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement result");
         new DeadCodeEliminationPhase(Required).apply(graph);
         /*
          * There must not be any parameter nodes left after OSR compilation.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/EliminateRedundantInitializationPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/EliminateRedundantInitializationPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -28,9 +28,6 @@
 import java.util.HashMap;
 import java.util.Map.Entry;
 
-import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
-import jdk.vm.ci.meta.JavaConstant;
-
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode;
@@ -43,6 +40,9 @@
 import org.graalvm.compiler.phases.BasePhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 
+import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
+import jdk.vm.ci.meta.Constant;
+
 public class EliminateRedundantInitializationPhase extends BasePhase<PhaseContext> {
     /**
      * Find blocks with class initializing nodes for the class identified the by the constant node.
@@ -204,7 +204,7 @@
         ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, false, true, false);
         ArrayList<Node> redundantInits = new ArrayList<>();
         for (ConstantNode node : getConstantNodes(graph)) {
-            JavaConstant constant = node.asJavaConstant();
+            Constant constant = node.asConstant();
             if (constant instanceof HotSpotMetaspaceConstant) {
                 redundantInits.addAll(processConstantNode(cfg, node));
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,8 +25,11 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_4;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
 
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.calc.Condition;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
@@ -42,6 +45,7 @@
 import org.graalvm.compiler.nodes.extended.GetClassNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -69,19 +73,30 @@
         this.clazz = clazz;
     }
 
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (tool.allUsagesAvailable() && hasNoUsages()) {
+    public static ValueNode create(ValueNode clazz, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, boolean allUsagesAvailable) {
+        return canonical(null, metaAccess, constantReflection, allUsagesAvailable, KlassPointerStamp.klass(), clazz);
+    }
+
+    @SuppressWarnings("unused")
+    public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode clazz) {
+        ValueNode clazzValue = create(clazz, b.getMetaAccess(), b.getConstantReflection(), false);
+        b.push(JavaKind.Object, b.recursiveAppend(clazzValue));
+        return true;
+    }
+
+    public static ValueNode canonical(ClassGetHubNode classGetHubNode, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, boolean allUsagesAvailable, Stamp stamp,
+                    ValueNode clazz) {
+        ClassGetHubNode self = classGetHubNode;
+        if (allUsagesAvailable && self != null && self.hasNoUsages()) {
             return null;
         } else {
             if (clazz.isConstant()) {
-                MetaAccessProvider metaAccess = tool.getMetaAccess();
                 if (metaAccess != null) {
-                    ResolvedJavaType exactType = tool.getConstantReflection().asJavaType(clazz.asJavaConstant());
+                    ResolvedJavaType exactType = constantReflection.asJavaType(clazz.asJavaConstant());
                     if (exactType.isPrimitive()) {
-                        return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, metaAccess);
+                        return ConstantNode.forConstant(stamp, JavaConstant.NULL_POINTER, metaAccess);
                     } else {
-                        return ConstantNode.forConstant(stamp(), tool.getConstantReflection().asObjectHub(exactType), metaAccess);
+                        return ConstantNode.forConstant(stamp, constantReflection.asObjectHub(exactType), metaAccess);
                     }
                 }
             }
@@ -90,14 +105,22 @@
                 return new LoadHubNode(KlassPointerStamp.klassNonNull(), getClass.getObject());
             }
             if (clazz instanceof HubGetClassNode) {
-                // replace _klass._java_mirror._klass -> _klass
+                // Replace: _klass._java_mirror._klass -> _klass
                 return ((HubGetClassNode) clazz).getHub();
             }
-            return this;
+            if (self == null) {
+                self = new ClassGetHubNode(clazz);
+            }
+            return self;
         }
     }
 
     @Override
+    public Node canonical(CanonicalizerTool tool) {
+        return canonical(this, tool.getMetaAccess(), tool.getConstantReflection(), tool.allUsagesAvailable(), stamp(), clazz);
+    }
+
+    @Override
     public void lower(LoweringTool tool) {
         tool.getLowerer().lower(this, tool);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java	Thu Apr 06 14:31:32 2017 -0700
@@ -702,17 +702,17 @@
 
     public static Word loadWordFromObject(Object object, int offset) {
         ReplacementsUtil.staticAssert(offset != hubOffset(INJECTED_VMCONFIG), "Use loadHubIntrinsic instead of loadWordFromObject");
-        return loadWordFromObjectIntrinsic(object, offset, getWordKind(), LocationIdentity.any());
+        return loadWordFromObjectIntrinsic(object, offset, LocationIdentity.any(), getWordKind());
     }
 
     public static Word loadWordFromObject(Object object, int offset, LocationIdentity identity) {
         ReplacementsUtil.staticAssert(offset != hubOffset(INJECTED_VMCONFIG), "Use loadHubIntrinsic instead of loadWordFromObject");
-        return loadWordFromObjectIntrinsic(object, offset, getWordKind(), identity);
+        return loadWordFromObjectIntrinsic(object, offset, identity, getWordKind());
     }
 
     public static KlassPointer loadKlassFromObject(Object object, int offset, LocationIdentity identity) {
         ReplacementsUtil.staticAssert(offset != hubOffset(INJECTED_VMCONFIG), "Use loadHubIntrinsic instead of loadWordFromObject");
-        return loadKlassFromObjectIntrinsic(object, offset, getWordKind(), identity);
+        return loadKlassFromObjectIntrinsic(object, offset, identity, getWordKind());
     }
 
     /**
@@ -725,17 +725,17 @@
         return registerAsWord(register, true, false);
     }
 
-    @NodeIntrinsic(value = ReadRegisterNode.class, setStampFromReturnType = true)
+    @NodeIntrinsic(value = ReadRegisterNode.class)
     public static native Word registerAsWord(@ConstantNodeParameter Register register, @ConstantNodeParameter boolean directUse, @ConstantNodeParameter boolean incoming);
 
-    @NodeIntrinsic(value = WriteRegisterNode.class, setStampFromReturnType = true)
+    @NodeIntrinsic(value = WriteRegisterNode.class)
     public static native void writeRegisterAsWord(@ConstantNodeParameter Register register, Word value);
 
-    @NodeIntrinsic(value = RawLoadNode.class, setStampFromReturnType = true)
-    private static native Word loadWordFromObjectIntrinsic(Object object, long offset, @ConstantNodeParameter JavaKind wordKind, @ConstantNodeParameter LocationIdentity locationIdentity);
+    @NodeIntrinsic(value = RawLoadNode.class)
+    private static native Word loadWordFromObjectIntrinsic(Object object, long offset, @ConstantNodeParameter LocationIdentity locationIdentity, @ConstantNodeParameter JavaKind wordKind);
 
-    @NodeIntrinsic(value = RawLoadNode.class, setStampFromReturnType = true)
-    private static native KlassPointer loadKlassFromObjectIntrinsic(Object object, long offset, @ConstantNodeParameter JavaKind wordKind, @ConstantNodeParameter LocationIdentity locationIdentity);
+    @NodeIntrinsic(value = RawLoadNode.class)
+    private static native KlassPointer loadKlassFromObjectIntrinsic(Object object, long offset, @ConstantNodeParameter LocationIdentity locationIdentity, @ConstantNodeParameter JavaKind wordKind);
 
     @NodeIntrinsic(value = LoadHubNode.class)
     public static native KlassPointer loadHubIntrinsic(Object object);
@@ -803,6 +803,8 @@
 
     public static final LocationIdentity CLASS_MIRROR_LOCATION = NamedLocationIdentity.immutable("Klass::_java_mirror");
 
+    public static final LocationIdentity CLASS_MIRROR_HANDLE_LOCATION = NamedLocationIdentity.immutable("Klass::_java_mirror handle");
+
     public static final LocationIdentity HEAP_TOP_LOCATION = NamedLocationIdentity.mutable("HeapTop");
 
     @Fold
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,7 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_4;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
 
+import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
 import org.graalvm.compiler.graph.Node;
@@ -70,7 +71,7 @@
             return null;
         } else {
             MetaAccessProvider metaAccess = tool.getMetaAccess();
-            if (metaAccess != null && hub.isConstant()) {
+            if (metaAccess != null && hub.isConstant() && !GraalOptions.ImmutableCode.getValue(graph().getOptions())) {
                 ResolvedJavaType exactType = tool.getConstantReflection().asJavaType(hub.asConstant());
                 if (exactType != null) {
                     return ConstantNode.forConstant(tool.getConstantReflection().asJavaClass(exactType), metaAccess);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,9 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_4;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
 
+import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
@@ -38,6 +41,7 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 
@@ -63,6 +67,18 @@
         this.klass = klass;
     }
 
+    public static ValueNode create(GraalHotSpotVMConfig config, ValueNode klass, ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess) {
+        Stamp stamp = StampFactory.forKind(JavaKind.Int);
+        return canonical(null, config, klass, stamp, constantReflection, metaAccess);
+    }
+
+    @SuppressWarnings("unused")
+    public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, @InjectedNodeParameter GraalHotSpotVMConfig config, ValueNode klass) {
+        ValueNode valueNode = create(config, klass, b.getConstantReflection(), b.getMetaAccess());
+        b.push(JavaKind.Int, b.recursiveAppend(valueNode));
+        return true;
+    }
+
     @Override
     public boolean inferStamp() {
         if (klass instanceof LoadHubNode) {
@@ -92,27 +108,36 @@
         if (tool.allUsagesAvailable() && hasNoUsages()) {
             return null;
         } else {
-            if (klass.isConstant()) {
-                if (!klass.asConstant().isDefaultForKind()) {
-                    Constant constant = stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), klass.asConstant(), config.klassLayoutHelperOffset);
-                    return ConstantNode.forConstant(stamp(), constant, tool.getMetaAccess());
+            return canonical(this, config, klass, stamp(), tool.getConstantReflection(), tool.getMetaAccess());
+        }
+    }
+
+    private static ValueNode canonical(KlassLayoutHelperNode klassLayoutHelperNode, GraalHotSpotVMConfig config, ValueNode klass, Stamp stamp, ConstantReflectionProvider constantReflection,
+                    MetaAccessProvider metaAccess) {
+        KlassLayoutHelperNode self = klassLayoutHelperNode;
+        if (klass.isConstant()) {
+            if (!klass.asConstant().isDefaultForKind()) {
+                Constant constant = stamp.readConstant(constantReflection.getMemoryAccessProvider(), klass.asConstant(), config.klassLayoutHelperOffset);
+                return ConstantNode.forConstant(stamp, constant, metaAccess);
+            }
+        }
+        if (klass instanceof LoadHubNode) {
+            LoadHubNode hub = (LoadHubNode) klass;
+            Stamp hubStamp = hub.getValue().stamp();
+            if (hubStamp instanceof ObjectStamp) {
+                ObjectStamp ostamp = (ObjectStamp) hubStamp;
+                HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) ostamp.type();
+                if (type != null && type.isArray() && !type.getComponentType().isPrimitive()) {
+                    // The layout for all object arrays is the same.
+                    Constant constant = stamp.readConstant(constantReflection.getMemoryAccessProvider(), type.klass(), config.klassLayoutHelperOffset);
+                    return ConstantNode.forConstant(stamp, constant, metaAccess);
                 }
             }
-            if (klass instanceof LoadHubNode) {
-                LoadHubNode hub = (LoadHubNode) klass;
-                Stamp hubStamp = hub.getValue().stamp();
-                if (hubStamp instanceof ObjectStamp) {
-                    ObjectStamp ostamp = (ObjectStamp) hubStamp;
-                    HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) ostamp.type();
-                    if (type != null && type.isArray() && !type.getComponentType().isPrimitive()) {
-                        // The layout for all object arrays is the same.
-                        Constant constant = stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), type.klass(), config.klassLayoutHelperOffset);
-                        return ConstantNode.forConstant(stamp(), constant, tool.getMetaAccess());
-                    }
-                }
-            }
-            return this;
         }
+        if (self == null) {
+            self = new KlassLayoutHelperNode(config, klass);
+        }
+        return self;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/LoadExceptionObjectSnippets.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/LoadExceptionObjectSnippets.java	Thu Apr 06 14:31:32 2017 -0700
@@ -35,8 +35,10 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
+import org.graalvm.compiler.hotspot.word.HotSpotWordTypes;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
 import org.graalvm.compiler.nodes.java.LoadExceptionObjectNode;
@@ -52,6 +54,7 @@
 import jdk.vm.ci.code.BytecodeFrame;
 import jdk.vm.ci.code.Register;
 import jdk.vm.ci.code.TargetDescription;
+import jdk.vm.ci.meta.ResolvedJavaType;
 
 /**
  * Snippet for loading the exception object at the start of an exception dispatcher.
@@ -76,15 +79,19 @@
     public static class Templates extends AbstractTemplates {
 
         private final SnippetInfo loadException = snippet(LoadExceptionObjectSnippets.class, "loadException", EXCEPTION_OOP_LOCATION, EXCEPTION_PC_LOCATION);
+        private final HotSpotWordTypes wordTypes;
 
         public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
             super(options, providers, providers.getSnippetReflection(), target);
+            this.wordTypes = providers.getWordTypes();
         }
 
         public void lower(LoadExceptionObjectNode loadExceptionObject, HotSpotRegistersProvider registers, LoweringTool tool) {
             StructuredGraph graph = loadExceptionObject.graph();
             if (LoadExceptionObjectInVM.getValue(graph.getOptions())) {
-                ReadRegisterNode thread = graph.add(new ReadRegisterNode(registers.getThreadRegister(), true, false));
+                ResolvedJavaType wordType = providers.getMetaAccess().lookupJavaType(Word.class);
+                Stamp stamp = wordTypes.getWordStamp(wordType);
+                ReadRegisterNode thread = graph.add(new ReadRegisterNode(stamp, registers.getThreadRegister(), true, false));
                 graph.addBeforeFixed(loadExceptionObject, thread);
                 ForeignCallNode loadExceptionC = graph.add(new ForeignCallNode(providers.getForeignCalls(), LOAD_AND_CLEAR_EXCEPTION, thread));
                 loadExceptionC.setStateAfter(loadExceptionObject.stateAfter());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java	Thu Apr 06 14:31:32 2017 -0700
@@ -56,7 +56,7 @@
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writeTlabTop;
 import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.ProfileAllocations;
 import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.ProfileAllocationsContext;
-import static org.graalvm.compiler.nodes.PiArrayNode.piArrayCast;
+import static org.graalvm.compiler.nodes.PiArrayNode.piArrayCastToSnippetReplaceeStamp;
 import static org.graalvm.compiler.nodes.PiNode.piCastToSnippetReplaceeStamp;
 import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY;
 import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FREQUENT_PROBABILITY;
@@ -226,7 +226,7 @@
         return verifyOop(result);
     }
 
-    @NodeIntrinsic(value = ForeignCallNode.class, returnStampIsNonNull = true)
+    @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
     public static native Object newInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub);
 
     @Snippet
@@ -307,7 +307,7 @@
                     @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter boolean maybeUnroll, @ConstantParameter String typeContext,
                     @ConstantParameter OptionValues options, @ConstantParameter Counters counters) {
         Object result = allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext, false, options, counters);
-        return piArrayCast(verifyOop(result), length);
+        return piArrayCastToSnippetReplaceeStamp(verifyOop(result), length);
     }
 
     private static Object allocateArrayImpl(KlassPointer hub, int length, Word prototypeMarkWord, int headerSize, int log2ElementSize, boolean fillContents, Register threadRegister,
@@ -334,20 +334,20 @@
         return result;
     }
 
-    @NodeIntrinsic(value = ForeignCallNode.class, returnStampIsNonNull = true)
+    @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
     public static native Object newArray(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int length, boolean fillContents);
 
     public static final ForeignCallDescriptor DYNAMIC_NEW_ARRAY = new ForeignCallDescriptor("dynamic_new_array", Object.class, Class.class, int.class);
     public static final ForeignCallDescriptor DYNAMIC_NEW_INSTANCE = new ForeignCallDescriptor("dynamic_new_instance", Object.class, Class.class);
 
-    @NodeIntrinsic(value = ForeignCallNode.class, returnStampIsNonNull = true)
+    @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
     public static native Object dynamicNewArrayStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType, int length);
 
     public static Object dynamicNewInstanceStub(Class<?> elementType) {
         return dynamicNewInstanceStubCall(DYNAMIC_NEW_INSTANCE, elementType);
     }
 
-    @NodeIntrinsic(value = ForeignCallNode.class, returnStampIsNonNull = true)
+    @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
     public static native Object dynamicNewInstanceStubCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType);
 
     @Snippet
@@ -396,7 +396,7 @@
         int log2ElementSize = (layoutHelper >> layoutHelperLog2ElementSizeShift(INJECTED_VMCONFIG)) & layoutHelperLog2ElementSizeMask(INJECTED_VMCONFIG);
 
         Object result = allocateArrayImpl(nonNullKlass, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, false, "dynamic type", true, options, counters);
-        return piArrayCast(verifyOop(result), length);
+        return piArrayCastToSnippetReplaceeStamp(verifyOop(result), length);
     }
 
     /**
@@ -418,7 +418,7 @@
         return newmultiarray(hubPIC, rank, dimensions);
     }
 
-    @NodeIntrinsic(value = ForeignCallNode.class, returnStampIsNonNull = true)
+    @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
     public static native Object newArrayCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int rank, Word dims);
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ReflectionGetCallerClassNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ReflectionGetCallerClassNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -80,7 +80,7 @@
 
     /**
      * If inlining is deep enough this method returns a {@link ConstantNode} of the caller class by
-     * walking the the stack.
+     * walking the stack.
      *
      * @param metaAccess
      * @return ConstantNode of the caller class, or null
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,8 +22,6 @@
  */
 package org.graalvm.compiler.hotspot.replacements.arraycopy;
 
-import jdk.vm.ci.meta.JavaKind;
-
 import static org.graalvm.compiler.core.common.LocationIdentity.any;
 
 import org.graalvm.compiler.core.common.LocationIdentity;
@@ -33,11 +31,12 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.nodes.spi.Virtualizable;
 import org.graalvm.compiler.replacements.nodes.BasicArrayCopyNode;
 
+import jdk.vm.ci.meta.JavaKind;
+
 @NodeInfo
-public final class ArrayCopyNode extends BasicArrayCopyNode implements Virtualizable, Lowerable {
+public final class ArrayCopyNode extends BasicArrayCopyNode implements Lowerable {
 
     public static final NodeClass<ArrayCopyNode> TYPE = NodeClass.create(ArrayCopyNode.class);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Thu Apr 06 14:31:32 2017 -0700
@@ -63,6 +63,7 @@
 import org.graalvm.compiler.nodes.java.ArrayLengthNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.nodes.type.StampTool;
+import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.replacements.SnippetCounter;
 import org.graalvm.compiler.replacements.SnippetCounter.Group;
@@ -628,7 +629,7 @@
         private void instantiate(Arguments args, BasicArrayCopyNode arraycopy) {
             StructuredGraph graph = arraycopy.graph();
             SnippetTemplate template = template(args);
-            UnmodifiableEconomicMap<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
+            UnmodifiableEconomicMap<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args, false);
             for (Node originalNode : replacements.getKeys()) {
                 if (originalNode instanceof Invoke) {
                     Invoke invoke = (Invoke) replacements.get(originalNode);
@@ -643,17 +644,18 @@
                     if (arraycopy.stateDuring() != null) {
                         newInvoke.setStateDuring(arraycopy.stateDuring());
                     } else {
-                        assert arraycopy.stateAfter() != null;
+                        assert arraycopy.stateAfter() != null : arraycopy;
                         newInvoke.setStateAfter(arraycopy.stateAfter());
                     }
                     graph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke);
                 } else if (originalNode instanceof ArrayCopySlowPathNode) {
                     ArrayCopySlowPathNode slowPath = (ArrayCopySlowPathNode) replacements.get(originalNode);
-                    assert arraycopy.stateAfter() != null;
-                    slowPath.setStateAfter(arraycopy.stateAfter());
+                    assert arraycopy.stateAfter() != null : arraycopy;
+                    assert slowPath.stateAfter() == arraycopy.stateAfter();
                     slowPath.setBci(arraycopy.getBci());
                 }
             }
+            GraphUtil.killCFG(arraycopy);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java	Thu Apr 06 14:31:32 2017 -0700
@@ -157,6 +157,6 @@
 
     public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_PC = newDescriptor(ExceptionHandlerStub.class, "exceptionHandlerForPc", Word.class, Word.class);
 
-    @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true)
+    @NodeIntrinsic(value = StubForeignCallNode.class)
     public static native Word exceptionHandlerForPc(@ConstantNodeParameter ForeignCallDescriptor exceptionHandlerForPc, Word thread);
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,6 +32,7 @@
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
 import org.graalvm.compiler.debug.Debug;
@@ -57,6 +58,7 @@
 
 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
 import jdk.vm.ci.hotspot.HotSpotSignature;
+import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.JavaMethod;
 import jdk.vm.ci.meta.JavaType;
 import jdk.vm.ci.meta.MetaAccessProvider;
@@ -241,17 +243,13 @@
         }
         kit.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));
 
-        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
-            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Initial stub graph");
-        }
+        Debug.dump(Debug.VERBOSE_LEVEL, graph, "Initial stub graph");
 
         kit.inlineInvokes();
 
         new RemoveValueProxyPhase().apply(graph);
 
-        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
-            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Stub graph before compilation");
-        }
+        Debug.dump(Debug.VERBOSE_LEVEL, graph, "Stub graph before compilation");
 
         return graph;
     }
@@ -269,13 +267,14 @@
     }
 
     private StubForeignCallNode createTargetCall(GraphKit kit, ParameterNode[] params, ReadRegisterNode thread) {
+        Stamp stamp = StampFactory.forKind(JavaKind.fromJavaClass(target.getDescriptor().getResultType()));
         if (prependThread) {
             ValueNode[] targetArguments = new ValueNode[1 + params.length];
             targetArguments[0] = thread;
             System.arraycopy(params, 0, targetArguments, 1, params.length);
-            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), targetArguments));
+            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), stamp, target.getDescriptor(), targetArguments));
         } else {
-            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), params));
+            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), stamp, target.getDescriptor(), params));
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/Stub.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/Stub.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,7 @@
 import static org.graalvm.compiler.core.GraalCompiler.emitBackEnd;
 import static org.graalvm.compiler.core.GraalCompiler.emitFrontEnd;
 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugStubsAndSnippets;
 import static org.graalvm.compiler.hotspot.HotSpotHostBackend.UNCOMMON_TRAP_HANDLER;
 import static org.graalvm.util.CollectionsUtil.allMatch;
 
@@ -35,6 +36,7 @@
 import org.graalvm.compiler.core.target.Backend;
 import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugConfig;
 import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
@@ -176,7 +178,8 @@
     @SuppressWarnings("try")
     public synchronized InstalledCode getCode(final Backend backend) {
         if (code == null) {
-            try (Scope d = Debug.sandbox("CompilingStub", DebugScope.getConfig(), providers.getCodeCache(), debugScopeContext())) {
+            DebugConfig config = DebugStubsAndSnippets.getValue(options) ? DebugScope.getConfig() : Debug.silentConfig();
+            try (Scope d = Debug.sandbox("CompilingStub", config, providers.getCodeCache(), debugScopeContext())) {
                 CodeCacheProvider codeCache = providers.getCodeCache();
 
                 CompilationResult compResult = buildCompilationResult(backend);
@@ -184,7 +187,7 @@
                     assert destroyedCallerRegisters != null;
                     // Add a GeneratePIC check here later, we don't want to install
                     // code if we don't have a corresponding VM global symbol.
-                    HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(null, null, compResult);
+                    HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, null, null, compResult);
                     code = codeCache.installCode(null, compiledCode, null, null, false);
                 } catch (Throwable e) {
                     throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.java	Thu Apr 06 14:31:32 2017 -0700
@@ -122,6 +122,6 @@
     public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = newDescriptor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress", Word.class, Word.class,
                     Word.class);
 
-    @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true)
+    @NodeIntrinsic(value = StubForeignCallNode.class)
     public static native Word exceptionHandlerForReturnAddress(@ConstantNodeParameter ForeignCallDescriptor exceptionHandlerForReturnAddress, Word thread, Word returnAddress);
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java	Thu Apr 06 14:31:32 2017 -0700
@@ -1051,8 +1051,8 @@
     public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options) {
         BciBlockMapping map = new BciBlockMapping(code);
         map.build(stream, options);
-        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
-            Debug.dump(Debug.INFO_LOG_LEVEL, map, code.getMethod().format("After block building %f %R %H.%n(%P)"));
+        if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
+            Debug.dump(Debug.INFO_LEVEL, map, code.getMethod().format("After block building %f %R %H.%n(%P)"));
         }
 
         return map;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Thu Apr 06 14:31:32 2017 -0700
@@ -247,7 +247,6 @@
 import static org.graalvm.compiler.core.common.type.StampFactory.objectNonNull;
 import static org.graalvm.compiler.debug.GraalError.guarantee;
 import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere;
-import static org.graalvm.compiler.java.BytecodeParserOptions.DumpDuringGraphBuilding;
 import static org.graalvm.compiler.java.BytecodeParserOptions.TraceBytecodeParserLevel;
 import static org.graalvm.compiler.java.BytecodeParserOptions.TraceInlineDuringParsing;
 import static org.graalvm.compiler.java.BytecodeParserOptions.TraceParserPlugins;
@@ -272,8 +271,8 @@
 import org.graalvm.compiler.bytecode.Bytes;
 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecodeProvider;
+import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.PermanentBailoutException;
-import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.common.calc.FloatConvert;
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
@@ -770,10 +769,6 @@
             for (BciBlock block : blocks) {
                 processBlock(block);
             }
-
-            if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL) && DumpDuringGraphBuilding.getValue(options) && this.beforeReturnNode != startInstruction) {
-                Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Bytecodes parsed: %s.%s", method.getDeclaringClass().getUnqualifiedName(), method.getName());
-            }
         }
     }
 
@@ -890,7 +885,7 @@
      */
     protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) {
         assert !graphBuilderConfig.eagerResolving();
-        append(new FixedGuardNode(graph.unique(IsNullNode.create(object)), Unresolved, InvalidateRecompile));
+        append(new FixedGuardNode(graph.addOrUniqueWithInputs(IsNullNode.create(object)), Unresolved, InvalidateRecompile));
         frameState.push(JavaKind.Object, appendConstant(JavaConstant.NULL_POINTER));
     }
 
@@ -902,7 +897,7 @@
         assert !graphBuilderConfig.eagerResolving();
         AbstractBeginNode successor = graph.add(new BeginNode());
         DeoptimizeNode deopt = graph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-        append(new IfNode(graph.unique(IsNullNode.create(object)), successor, deopt, 1));
+        append(new IfNode(graph.addOrUniqueWithInputs(IsNullNode.create(object)), successor, deopt, 1));
         lastInstr = successor;
         frameState.push(JavaKind.Int, appendConstant(JavaConstant.INT_0));
     }
@@ -1060,15 +1055,15 @@
     }
 
     protected ValueNode genNegateOp(ValueNode x) {
-        return (new NegateNode(x));
+        return NegateNode.create(x);
     }
 
     protected ValueNode genLeftShift(ValueNode x, ValueNode y) {
-        return new LeftShiftNode(x, y);
+        return LeftShiftNode.create(x, y);
     }
 
     protected ValueNode genRightShift(ValueNode x, ValueNode y) {
-        return new RightShiftNode(x, y);
+        return RightShiftNode.create(x, y);
     }
 
     protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) {
@@ -1123,11 +1118,11 @@
     }
 
     protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) {
-        return IntegerEqualsNode.create(x, y, constantReflection);
+        return IntegerEqualsNode.create(x, y);
     }
 
     protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) {
-        return IntegerLessThanNode.create(x, y, constantReflection);
+        return IntegerLessThanNode.create(x, y);
     }
 
     protected ValueNode genUnique(ValueNode x) {
@@ -1146,8 +1141,8 @@
         genInfoPointNode(InfopointReason.BYTECODE_POSITION, null);
 
         ValueNode exception = frameState.pop(JavaKind.Object);
-        FixedGuardNode nullCheck = append(new FixedGuardNode(graph.unique(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true));
-        PiNode nonNullException = graph.unique(new PiNode(exception, exception.stamp().join(objectNonNull()), nullCheck));
+        FixedGuardNode nullCheck = append(new FixedGuardNode(graph.addOrUniqueWithInputs(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true));
+        ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp().join(objectNonNull()), nullCheck));
         lastInstr.setNext(handleException(nonNullException, bci()));
     }
 
@@ -1172,7 +1167,7 @@
     }
 
     protected ValueNode genConditional(ValueNode x) {
-        return new ConditionalNode((LogicNode) x);
+        return ConditionalNode.create((LogicNode) x);
     }
 
     protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) {
@@ -1190,9 +1185,11 @@
     protected ValueNode genLoadField(ValueNode receiver, ResolvedJavaField field) {
         StampPair stamp = graphBuilderConfig.getPlugins().getOverridingStamp(this, field.getType(), false);
         if (stamp == null) {
-            return LoadFieldNode.create(this.graph.getAssumptions(), receiver, field);
+            return LoadFieldNode.create(getConstantFieldProvider(), getConstantReflection(), getMetaAccess(), getOptions(),
+                            getAssumptions(), receiver, field, false, false);
         } else {
-            return LoadFieldNode.createOverrideStamp(stamp, receiver, field);
+            return LoadFieldNode.createOverrideStamp(getConstantFieldProvider(), getConstantReflection(), getMetaAccess(), getOptions(),
+                            stamp, receiver, field, false, false);
         }
     }
 
@@ -1202,8 +1199,8 @@
         }
         BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class));
         AbstractBeginNode falseSucc = graph.add(new BeginNode());
-        PiNode nonNullReceiver = graph.unique(new PiNode(receiver, objectNonNull(), falseSucc));
-        append(new IfNode(graph.unique(IsNullNode.create(receiver)), exception, falseSucc, 0.01));
+        ValueNode nonNullReceiver = graph.addOrUnique(PiNode.create(receiver, objectNonNull(), falseSucc));
+        append(new IfNode(graph.addOrUniqueWithInputs(IsNullNode.create(receiver)), exception, falseSucc, 0.01));
         lastInstr = falseSucc;
 
         exception.setStateAfter(createFrameState(bci(), exception));
@@ -1215,7 +1212,7 @@
     protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) {
         AbstractBeginNode trueSucc = graph.add(new BeginNode());
         BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
-        append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
+        append(new IfNode(genUnique(IntegerBelowNode.create(index, length)), trueSucc, exception, 0.99));
         lastInstr = trueSucc;
 
         exception.setStateAfter(createFrameState(bci(), exception));
@@ -1401,11 +1398,6 @@
         }
         if (invokeKind.hasReceiver()) {
             args[0] = emitExplicitExceptions(args[0]);
-
-            if (args[0].isNullConstant()) {
-                append(new DeoptimizeNode(InvalidateRecompile, NullCheckException));
-                return null;
-            }
         }
 
         InlineInfo inlineInfo = null;
@@ -1419,6 +1411,11 @@
                 return null;
             }
 
+            if (invokeKind.hasReceiver() && args[0].isNullConstant()) {
+                append(new DeoptimizeNode(InvalidateRecompile, NullCheckException));
+                return null;
+            }
+
             if (!invokeKind.isIndirect() || (UseGuardedIntrinsics.getValue(options) && !GeneratePIC.getValue(options))) {
                 if (tryInvocationPlugin(invokeKind, args, targetMethod, resultType, returnType)) {
                     if (TraceParserPlugins.getValue(options)) {
@@ -1611,7 +1608,7 @@
             LoadHubNode hub = graph.unique(new LoadHubNode(stampProvider, nonNullReceiver));
             LoadMethodNode actual = append(new LoadMethodNode(methodStamp, targetMethod, receiverType, method.getDeclaringClass(), hub));
             ConstantNode expected = graph.unique(ConstantNode.forConstant(methodStamp, targetMethod.getEncoding(), getMetaAccess()));
-            LogicNode compare = graph.unique(CompareNode.createCompareNode(Condition.EQ, actual, expected, constantReflection));
+            LogicNode compare = graph.addOrUniqueWithInputs(CompareNode.createCompareNode(Condition.EQ, actual, expected, constantReflection));
 
             JavaTypeProfile profile = null;
             if (profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) {
@@ -2125,8 +2122,8 @@
         JsrScope scope = currentBlock.getJsrScope();
         int retAddress = scope.nextReturnAddress();
         ConstantNode returnBciNode = getJsrConstant(retAddress);
-        LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflection);
-        guard = graph.unique(guard);
+        LogicNode guard = IntegerEqualsNode.create(local, returnBciNode);
+        guard = graph.addOrUniqueWithInputs(guard);
         append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
         if (!successor.getJsrScope().equals(scope.pop())) {
             throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
@@ -2656,7 +2653,7 @@
     }
 
     private DebugCloseable openNodeContext() {
-        if (graphBuilderConfig.trackNodeSourcePosition() && !parsingIntrinsic()) {
+        if ((graphBuilderConfig.trackNodeSourcePosition() || Debug.isDumpEnabledForMethod()) && !parsingIntrinsic()) {
             return graph.withNodeSourcePosition(createBytecodePosition());
         }
         return null;
@@ -2776,7 +2773,7 @@
             genConstantTargetIf(trueBlock, falseBlock, negate, condition);
         } else {
             if (condition.graph() == null) {
-                condition = graph.unique(condition);
+                condition = genUnique(condition);
             }
 
             // Need to get probability based on current bci.
@@ -3048,7 +3045,7 @@
 
         int nextBCI = stream.nextBCI();
         int nextBC = stream.readUByte(nextBCI);
-        if (nextBC == Bytecodes.GETFIELD) {
+        if (nextBCI <= currentBlock.endBci && nextBC == Bytecodes.GETFIELD) {
             stream.next();
             genGetField(lookupField(stream.readCPI(), Bytecodes.GETFIELD), value);
         } else {
@@ -3147,7 +3144,7 @@
             default:
                 throw shouldNotReachHere();
         }
-        frameState.push(kind, append(v));
+        frameState.push(kind, recursiveAppend(v));
     }
 
     private void genIntegerDivOp(JavaKind kind, int opcode) {
@@ -3194,7 +3191,7 @@
             default:
                 throw shouldNotReachHere();
         }
-        frameState.push(kind, append(v));
+        frameState.push(kind, recursiveAppend(v));
     }
 
     private void genLogicOp(JavaKind kind, int opcode) {
@@ -3217,7 +3214,7 @@
             default:
                 throw shouldNotReachHere();
         }
-        frameState.push(kind, append(v));
+        frameState.push(kind, recursiveAppend(v));
     }
 
     private void genCompareOp(JavaKind kind, boolean isUnorderedLess) {
@@ -3236,7 +3233,7 @@
         if (from != from.getStackKind()) {
             input = append(genNarrow(input, from.getBitCount()));
         }
-        frameState.push(to, append(genSignExtend(input, to.getBitCount())));
+        frameState.push(to, recursiveAppend(genSignExtend(input, to.getBitCount())));
     }
 
     private void genZeroExtend(JavaKind from, JavaKind to) {
@@ -3257,7 +3254,7 @@
         int delta = getStream().readIncrement();
         ValueNode x = frameState.loadLocal(index, JavaKind.Int);
         ValueNode y = appendConstant(JavaConstant.forInt(delta));
-        frameState.storeLocal(index, JavaKind.Int, append(genIntegerAdd(x, y)));
+        frameState.storeLocal(index, JavaKind.Int, recursiveAppend(genIntegerAdd(x, y)));
     }
 
     private void genIfZero(Condition cond) {
@@ -3373,7 +3370,7 @@
                         castNode = object;
                     } else {
                         FixedGuardNode fixedGuard = append(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, false));
-                        castNode = append(new PiNode(object, StampFactory.objectNonNull(TypeReference.createExactTrusted(singleType)), fixedGuard));
+                        castNode = append(PiNode.create(object, StampFactory.objectNonNull(TypeReference.createExactTrusted(singleType)), fixedGuard));
                     }
                 }
             }
@@ -3386,7 +3383,7 @@
                 castNode = object;
             } else {
                 FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, DeoptimizationReason.ClassCastException, DeoptimizationAction.InvalidateReprofile, false));
-                castNode = append(new PiNode(object, StampFactory.object(checkedType, nonNull), fixedGuard));
+                castNode = append(PiNode.create(object, StampFactory.object(checkedType, nonNull), fixedGuard));
             }
         }
         frameState.push(JavaKind.Object, castNode);
@@ -3431,7 +3428,7 @@
 
         int next = getStream().nextBCI();
         int value = getStream().readUByte(next);
-        if (value == Bytecodes.IFEQ || value == Bytecodes.IFNE) {
+        if (next <= currentBlock.endBci && (value == Bytecodes.IFEQ || value == Bytecodes.IFNE)) {
             getStream().next();
             BciBlock firstSucc = currentBlock.getSuccessor(0);
             BciBlock secondSucc = currentBlock.getSuccessor(1);
@@ -3678,11 +3675,10 @@
     }
 
     private void genGetStatic(JavaField field) {
-        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-            handleUnresolvedLoadField(field, null);
+        ResolvedJavaField resolvedField = resolveStaticFieldAccess(field, null);
+        if (resolvedField == null) {
             return;
         }
-        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
 
         if (!parsingIntrinsic() && GeneratePIC.getValue(getOptions())) {
             graph.recordField(resolvedField);
@@ -3712,13 +3708,39 @@
         frameState.push(field.getJavaKind(), append(genLoadField(null, resolvedField)));
     }
 
+    private ResolvedJavaField resolveStaticFieldAccess(JavaField field, ValueNode value) {
+        if (field instanceof ResolvedJavaField) {
+            ResolvedJavaField resolvedField = (ResolvedJavaField) field;
+            if (resolvedField.getDeclaringClass().isInitialized()) {
+                return resolvedField;
+            }
+            /*
+             * Static fields have initialization semantics but may be safely accessed under certain
+             * conditions while the class is being initialized. Executing in the clinit or init of
+             * classes which are subtypes of the field holder are sure to be running in a context
+             * where the access is safe.
+             */
+            if (resolvedField.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
+                if (method.isClassInitializer() || method.isConstructor()) {
+                    return resolvedField;
+                }
+            }
+        }
+        if (value == null) {
+            handleUnresolvedLoadField(field, null);
+        } else {
+            handleUnresolvedStoreField(field, value, null);
+
+        }
+        return null;
+    }
+
     private void genPutStatic(JavaField field) {
         ValueNode value = frameState.pop(field.getJavaKind());
-        if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-            handleUnresolvedStoreField(field, value, null);
+        ResolvedJavaField resolvedField = resolveStaticFieldAccess(field, value);
+        if (resolvedField == null) {
             return;
         }
-        ResolvedJavaField resolvedField = (ResolvedJavaField) field;
 
         if (!parsingIntrinsic() && GeneratePIC.getValue(getOptions())) {
             graph.recordField(resolvedField);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java	Thu Apr 06 14:31:32 2017 -0700
@@ -52,9 +52,6 @@
     @Option(help = "Maximum depth when inlining during bytecode parsing.", type = OptionType.Debug)
     public static final OptionKey<Integer> InlineDuringParsingMaxDepth = new OptionKey<>(10);
 
-    @Option(help = "Dump graphs after non-trivial changes during bytecode parsing.", type = OptionType.Debug)
-    public static final OptionKey<Boolean> DumpDuringGraphBuilding = new OptionKey<>(false);
-
     @Option(help = "When creating info points hide the methods of the substitutions.", type = OptionType.Debug)
     public static final OptionKey<Boolean> HideSubstitutionStates = new OptionKey<>(false);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCControlFlow.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCControlFlow.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,6 +22,11 @@
  */
 package org.graalvm.compiler.lir.sparc;
 
+import static jdk.vm.ci.code.ValueUtil.asRegister;
+import static jdk.vm.ci.sparc.SPARC.CPU;
+import static jdk.vm.ci.sparc.SPARC.g0;
+import static jdk.vm.ci.sparc.SPARCKind.WORD;
+import static jdk.vm.ci.sparc.SPARCKind.XWORD;
 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CBCOND;
 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.FBPCC;
@@ -68,19 +73,14 @@
 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
 import static org.graalvm.compiler.lir.sparc.SPARCMove.const2reg;
 import static org.graalvm.compiler.lir.sparc.SPARCOP3Op.emitOp3;
-import static jdk.vm.ci.code.ValueUtil.asRegister;
-import static jdk.vm.ci.sparc.SPARC.CPU;
-import static jdk.vm.ci.sparc.SPARC.g0;
-import static jdk.vm.ci.sparc.SPARCKind.WORD;
-import static jdk.vm.ci.sparc.SPARCKind.XWORD;
 
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
+
 import org.graalvm.compiler.asm.Assembler;
 import org.graalvm.compiler.asm.Assembler.LabelHint;
 import org.graalvm.compiler.asm.Label;
-import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.asm.sparc.SPARCAssembler;
 import org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict;
 import org.graalvm.compiler.asm.sparc.SPARCAssembler.CC;
@@ -98,8 +98,8 @@
 import org.graalvm.compiler.lir.SwitchStrategy.BaseSwitchClosure;
 import org.graalvm.compiler.lir.Variable;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 
 import jdk.vm.ci.code.Register;
 import jdk.vm.ci.meta.AllocatableValue;
@@ -159,6 +159,7 @@
         public CompareBranchOp(AllocatableValue x, Value y, Condition condition, LabelRef trueDestination, LabelRef falseDestination, SPARCKind kind, boolean unorderedIsTrue,
                         double trueDestinationProbability) {
             super(TYPE, SIZE);
+            assert x.getPlatformKind() == y.getPlatformKind() : String.format("PlatformKind of x must match PlatformKind of y. %s!=%s", x.getPlatformKind(), y.getPlatformKind());
             this.x = x;
             this.y = y;
             this.trueDestination = trueDestination;
@@ -250,6 +251,7 @@
          * @return true if the branch could be emitted
          */
         private boolean emitShortCompareBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+            boolean isLong = kind == SPARCKind.XWORD;
             ConditionFlag actualConditionFlag = conditionFlag;
             Label actualTrueTarget = trueDestination.label();
             Label actualFalseTarget = falseDestination.label();
@@ -274,7 +276,7 @@
                     actualFalseTarget = tmpTarget;
                 }
             }
-            emitCBCond(masm, x, y, actualTrueTarget, actualConditionFlag);
+            emitCBCond(masm, x, y, actualTrueTarget, actualConditionFlag, isLong);
             if (needJump) {
                 masm.jmp(actualFalseTarget);
                 masm.nop();
@@ -282,16 +284,24 @@
             return true;
         }
 
-        private void emitCBCond(SPARCMacroAssembler masm, Value actualX, Value actualY, Label actualTrueTarget, ConditionFlag cFlag) {
+        private static void emitCBCond(SPARCMacroAssembler masm, Value actualX, Value actualY, Label actualTrueTarget, ConditionFlag cFlag, boolean isLong) {
             PlatformKind xKind = actualX.getPlatformKind();
-            boolean isLong = kind == SPARCKind.XWORD;
+            Register rs1 = asRegister(actualX, xKind);
             if (isJavaConstant(actualY)) {
                 JavaConstant c = asJavaConstant(actualY);
                 long constantY = c.isNull() ? 0 : c.asLong();
-                assert NumUtil.isInt(constantY);
-                CBCOND.emit(masm, cFlag, isLong, asRegister(actualX, xKind), (int) constantY, actualTrueTarget);
+                try (ScratchRegister scratch = masm.getScratchRegister()) {
+                    if (SPARCMacroAssembler.isSimm5(constantY)) {
+                        CBCOND.emit(masm, cFlag, isLong, rs1, (int) constantY, actualTrueTarget);
+                    } else { // !simm5
+                        Register rs2 = scratch.getRegister();
+                        masm.setx(constantY, rs2, false);
+                        CBCOND.emit(masm, cFlag, isLong, rs1, rs2, actualTrueTarget);
+                    }
+                }
             } else {
-                CBCOND.emit(masm, cFlag, isLong, asRegister(actualX, xKind), asRegister(actualY, xKind), actualTrueTarget);
+                Register rs2 = asRegister(actualY, xKind);
+                CBCOND.emit(masm, cFlag, isLong, rs1, rs2, actualTrueTarget);
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java	Thu Apr 06 14:31:32 2017 -0700
@@ -217,7 +217,7 @@
         /**
          * Calculates the entry and exit states for all basic blocks.
          *
-         * @return Returns true on success and false if the the control flow is too complex.
+         * @return Returns true on success and false if the control flow is too complex.
          */
         @SuppressWarnings("try")
         private boolean solveDataFlow(LIR lir) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/SwitchStrategy.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/SwitchStrategy.java	Thu Apr 06 14:31:32 2017 -0700
@@ -46,7 +46,7 @@
     private interface SwitchClosure {
         /**
          * Generates a conditional or unconditional jump. The jump will be unconditional if
-         * condition is null. If defaultTarget is true, then the jump will go the the default.
+         * condition is null. If defaultTarget is true, then the jump will go the default.
          *
          * @param index Index of the value and the jump target (only used if defaultTarget == false)
          * @param condition The condition on which to jump (can be null)
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java	Thu Apr 06 14:31:32 2017 -0700
@@ -750,11 +750,11 @@
                 }
             }
         }
-        Debug.dump(Debug.BASIC_LOG_LEVEL, new LinearScanIntervalDumper(Arrays.copyOf(intervals, intervalsSize)), label);
+        Debug.dump(Debug.BASIC_LEVEL, new LinearScanIntervalDumper(Arrays.copyOf(intervals, intervalsSize)), label);
     }
 
     public void printLir(String label, @SuppressWarnings("unused") boolean hirValid) {
-        Debug.dump(Debug.INFO_LOG_LEVEL, ir, label);
+        Debug.dump(Debug.INFO_LEVEL, ir, label);
     }
 
     boolean verify() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -110,12 +110,12 @@
                 interval.setSpillState(SpillState.StoreAtDefinition);
                 return;
             }
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "Spill block candidate (initial): %s", spillBlock);
+            Debug.log(Debug.VERBOSE_LEVEL, "Spill block candidate (initial): %s", spillBlock);
             // move out of loops
             if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) {
                 spillBlock = moveSpillOutOfLoop(defBlock, spillBlock);
             }
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "Spill block candidate (after loop optimizaton): %s", spillBlock);
+            Debug.log(Debug.VERBOSE_LEVEL, "Spill block candidate (after loop optimizaton): %s", spillBlock);
 
             /*
              * The spill block is the begin of the first split child (aka the value is on the
@@ -134,7 +134,7 @@
                 spillBlock = dom;
             }
             if (defBlock.equals(spillBlock)) {
-                Debug.log(Debug.VERBOSE_LOG_LEVEL, "Definition is the best choice: %s", defBlock);
+                Debug.log(Debug.VERBOSE_LEVEL, "Definition is the best choice: %s", defBlock);
                 // definition is the best choice
                 interval.setSpillState(SpillState.StoreAtDefinition);
                 return;
@@ -146,7 +146,7 @@
             }
 
             if (defBlock.probability() <= spillBlock.probability()) {
-                Debug.log(Debug.VERBOSE_LOG_LEVEL, "Definition has lower probability %s (%f) is lower than spill block %s (%f)", defBlock, defBlock.probability(), spillBlock,
+                Debug.log(Debug.VERBOSE_LEVEL, "Definition has lower probability %s (%f) is lower than spill block %s (%f)", defBlock, defBlock.probability(), spillBlock,
                                 spillBlock.probability());
                 // better spill block has the same probability -> do nothing
                 interval.setSpillState(SpillState.StoreAtDefinition);
@@ -164,7 +164,7 @@
             AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, allocator).location();
             AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval);
             LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation);
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "Insert spill move %s", move);
+            Debug.log(Debug.VERBOSE_LEVEL, "Insert spill move %s", move);
             move.setId(LinearScan.DOMINATOR_SPILL_MOVE_ID);
             /*
              * We can use the insertion buffer directly because we always insert at position 1.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanWalker.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanWalker.java	Thu Apr 06 14:31:32 2017 -0700
@@ -861,7 +861,7 @@
                          * errors
                          */
                         allocator.assignSpillSlot(interval);
-                        Debug.dump(Debug.INFO_LOG_LEVEL, allocator.getLIR(), description);
+                        Debug.dump(Debug.INFO_LEVEL, allocator.getLIR(), description);
                         allocator.printIntervals(description);
                         throw new OutOfRegistersException("LinearScan: no register found", description);
                     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessAnalysisPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessAnalysisPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -65,7 +65,7 @@
 
     private final class Analyser {
 
-        private static final int LOG_LEVEL = Debug.INFO_LOG_LEVEL;
+        private static final int LOG_LEVEL = Debug.INFO_LEVEL;
 
         /**
          * Bit map specifying which operands are live upon entry to this block. These are values
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceBuilderPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceBuilderPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -64,8 +64,8 @@
         // @formatter:on
     }
 
-    private static final int TRACE_LOG_LEVEL = 1;
-    public static final int TRACE_DUMP_LEVEL = 3;
+    private static final int TRACE_LOG_LEVEL = Debug.BASIC_LEVEL;
+    public static final int TRACE_DUMP_LEVEL = Debug.VERBOSE_LEVEL;
 
     @Override
     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -89,7 +89,7 @@
         final TraceRegisterAllocationPolicy plan = DefaultTraceRegisterAllocationPolicy.allocationPolicy(target, lirGenRes, spillMoveFactory, registerAllocationConfig, cachedStackSlots, resultTraces,
                         neverSpillConstant, livenessInfo, lir.getOptions());
 
-        Debug.dump(Debug.INFO_LOG_LEVEL, lir, "Before TraceRegisterAllocation");
+        Debug.dump(Debug.INFO_LEVEL, lir, "Before TraceRegisterAllocation");
         try (Scope s0 = Debug.scope("AllocateTraces", resultTraces, livenessInfo)) {
             for (Trace trace : resultTraces.getTraces()) {
                 tracesCounter.increment();
@@ -101,9 +101,9 @@
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
-        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+        if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
             unnumberInstructions(lir);
-            Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After trace allocation");
+            Debug.dump(Debug.INFO_LEVEL, lir, "After trace allocation");
         }
 
         TRACE_GLOBAL_MOVE_RESOLUTION_PHASE.apply(target, lirGenRes, traceContext);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -1073,7 +1073,7 @@
                         }
                     }
                 }
-                Debug.dump(Debug.INFO_LOG_LEVEL, this, label);
+                Debug.dump(Debug.INFO_LEVEL, this, label);
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java	Thu Apr 06 14:31:32 2017 -0700
@@ -954,7 +954,7 @@
                              * avoid errors
                              */
                             allocator.assignSpillSlot(interval);
-                            if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+                            if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
                                 dumpLIRAndIntervals(description);
                             }
                             throw new OutOfRegistersException("LinearScan: no register found", description);
@@ -991,7 +991,7 @@
     }
 
     protected void dumpLIRAndIntervals(String description) {
-        Debug.dump(Debug.INFO_LOG_LEVEL, allocator.getLIR(), description);
+        Debug.dump(Debug.INFO_LEVEL, allocator.getLIR(), description);
         allocator.printIntervals(description);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -461,7 +461,7 @@
         if (block == null) {
             return;
         }
-        boolean emitComment = Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL) || PrintLIRWithAssembly.getValue(getOptions());
+        boolean emitComment = Debug.isDumpEnabled(Debug.BASIC_LEVEL) || PrintLIRWithAssembly.getValue(getOptions());
         if (emitComment) {
             blockComment(String.format("block B%d %s", block.getId(), block.getLoop()));
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java	Thu Apr 06 14:31:32 2017 -0700
@@ -214,7 +214,7 @@
                                     phiConstantsSkipped.increment();
                                 }
                                 phiConstants.set(var.index);
-                                Debug.log(Debug.VERBOSE_LOG_LEVEL, "Removing phi variable: %s", var);
+                                Debug.log(Debug.VERBOSE_LEVEL, "Removing phi variable: %s", var);
                             }
                         } else {
                             assert defined.get(var.index) : "phi but not defined? " + var;
@@ -291,7 +291,7 @@
                 // no better solution found
                 materializeAtDefinitionSkipped.increment();
             }
-            Debug.dump(Debug.INFO_LOG_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
+            Debug.dump(Debug.DETAILED_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
         }
 
         private void createLoads(DefUseTree tree, ConstantTree constTree, AbstractBlockBase<?> startBlock) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTreeAnalyzer.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTreeAnalyzer.java	Thu Apr 06 14:31:32 2017 -0700
@@ -72,19 +72,19 @@
         worklist.offerLast(startBlock);
         while (!worklist.isEmpty()) {
             AbstractBlockBase<?> block = worklist.pollLast();
-            try (Indent i = Debug.logAndIndent(Debug.VERBOSE_LOG_LEVEL, "analyze: %s", block)) {
+            try (Indent i = Debug.logAndIndent(Debug.VERBOSE_LEVEL, "analyze: %s", block)) {
                 assert block != null : "worklist is empty!";
                 assert isMarked(block) : "Block not part of the dominator tree: " + block;
 
                 if (isLeafBlock(block)) {
-                    Debug.log(Debug.VERBOSE_LOG_LEVEL, "leaf block");
+                    Debug.log(Debug.VERBOSE_LEVEL, "leaf block");
                     leafCost(block);
                     continue;
                 }
 
                 if (!visited.get(block.getId())) {
                     // if not yet visited (and not a leaf block) process all children first!
-                    Debug.log(Debug.VERBOSE_LOG_LEVEL, "not marked");
+                    Debug.log(Debug.VERBOSE_LEVEL, "not marked");
                     worklist.offerLast(block);
                     AbstractBlockBase<?> dominated = block.getFirstDominated();
                     while (dominated != null) {
@@ -93,7 +93,7 @@
                     }
                     visited.set(block.getId());
                 } else {
-                    Debug.log(Debug.VERBOSE_LOG_LEVEL, "marked");
+                    Debug.log(Debug.VERBOSE_LEVEL, "marked");
                     // otherwise, process block
                     process(block);
                 }
@@ -162,7 +162,7 @@
 
     private void filteredPush(Deque<AbstractBlockBase<?>> worklist, AbstractBlockBase<?> block) {
         if (isMarked(block)) {
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "adding %s to the worklist", block);
+            Debug.log(Debug.VERBOSE_LEVEL, "adding %s to the worklist", block);
             worklist.offerLast(block);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -113,8 +113,8 @@
         try (Scope s = Debug.scope(getName(), this)) {
             try (DebugCloseable a = timer.start(); DebugCloseable c = memUseTracker.start()) {
                 run(target, lirGenRes, context);
-                if (dumpLIR && Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
-                    Debug.dump(Debug.BASIC_LOG_LEVEL, lirGenRes.getLIR(), "%s", getName());
+                if (dumpLIR && Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
+                    Debug.dump(Debug.BASIC_LEVEL, lirGenRes.getLIR(), "%s", getName());
                 }
             }
         } catch (Throwable e) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ssa/SSAVerifier.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ssa/SSAVerifier.java	Thu Apr 06 14:31:32 2017 -0700
@@ -87,7 +87,7 @@
                 doBlock(pred);
             }
         }
-        try (Indent indent = Debug.logAndIndent(Debug.INFO_LOG_LEVEL, "handle block %s", b)) {
+        try (Indent indent = Debug.logAndIndent(Debug.INFO_LEVEL, "handle block %s", b)) {
             assert verifyBlock(b);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/LSStackSlotAllocator.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/LSStackSlotAllocator.java	Thu Apr 06 14:31:32 2017 -0700
@@ -131,7 +131,7 @@
 
         @SuppressWarnings("try")
         private void allocate() {
-            Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After StackSlot numbering");
+            Debug.dump(Debug.INFO_LEVEL, lir, "After StackSlot numbering");
 
             long currentFrameSize = StackSlotAllocatorUtil.allocatedFramesize.isEnabled() ? frameMapBuilder.getFrameMap().currentFrameSize() : 0;
             EconomicSet<LIRInstruction> usePos;
@@ -145,14 +145,14 @@
                     assert verifyIntervals();
                 }
             }
-            if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+            if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
                 dumpIntervals("Before stack slot allocation");
             }
             // step 4: allocate stack slots
             try (DebugCloseable t = AllocateSlotsTimer.start()) {
                 allocateStackSlots();
             }
-            if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+            if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
                 dumpIntervals("After stack slot allocation");
             }
 
@@ -160,7 +160,7 @@
             try (DebugCloseable t = AssignSlotsTimer.start()) {
                 assignStackSlots(usePos);
             }
-            Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After StackSlot assignment");
+            Debug.dump(Debug.INFO_LEVEL, lir, "After StackSlot assignment");
             if (StackSlotAllocatorUtil.allocatedFramesize.isEnabled()) {
                 StackSlotAllocatorUtil.allocatedFramesize.add(frameMapBuilder.getFrameMap().currentFrameSize() - currentFrameSize);
             }
@@ -256,13 +256,13 @@
                      */
                     location = StackSlot.get(current.kind(), slot.getRawOffset(), slot.getRawAddFrameSize());
                     StackSlotAllocatorUtil.reusedSlots.increment();
-                    Debug.log(Debug.BASIC_LOG_LEVEL, "Reuse stack slot %s (reallocated from %s) for virtual stack slot %s", location, slot, virtualSlot);
+                    Debug.log(Debug.BASIC_LEVEL, "Reuse stack slot %s (reallocated from %s) for virtual stack slot %s", location, slot, virtualSlot);
                 } else {
                     // Allocate new stack slot.
                     location = frameMapBuilder.getFrameMap().allocateSpillSlot(virtualSlot.getValueKind());
                     StackSlotAllocatorUtil.virtualFramesize.add(frameMapBuilder.getFrameMap().spillSlotSize(virtualSlot.getValueKind()));
                     StackSlotAllocatorUtil.allocatedSlots.increment();
-                    Debug.log(Debug.BASIC_LOG_LEVEL, "New stack slot %s for virtual stack slot %s", location, virtualSlot);
+                    Debug.log(Debug.BASIC_LEVEL, "New stack slot %s for virtual stack slot %s", location, virtualSlot);
                 }
             }
             Debug.log("Allocate location %s for interval %s", location, current);
@@ -434,7 +434,7 @@
         }
 
         private void dumpIntervals(String label) {
-            Debug.dump(Debug.INFO_LOG_LEVEL, new StackIntervalDumper(Arrays.copyOf(stackSlotMap, stackSlotMap.length)), label);
+            Debug.dump(Debug.INFO_LEVEL, new StackIntervalDumper(Arrays.copyOf(stackSlotMap, stackSlotMap.length)), label);
         }
 
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopFullUnrollPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopFullUnrollPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -54,7 +54,7 @@
                         Debug.log("FullUnroll %s", loop);
                         LoopTransformations.fullUnroll(loop, context, canonicalizer);
                         FULLY_UNROLLED_LOOPS.increment();
-                        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "FullUnroll %s", loop);
+                        Debug.dump(Debug.DETAILED_LEVEL, graph, "FullUnroll %s", loop);
                         peeled = true;
                         break;
                     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -45,7 +45,7 @@
                     if (getPolicies().shouldPeel(loop, data.getCFG(), context.getMetaAccess())) {
                         Debug.log("Peeling %s", loop);
                         LoopTransformations.peel(loop);
-                        Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Peeling %s", loop);
+                        Debug.dump(Debug.DETAILED_LEVEL, graph, "Peeling %s", loop);
                     }
                 }
                 data.deleteUnusedNodes();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopUnswitchingPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopUnswitchingPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -61,7 +61,7 @@
                                     logUnswitch(loop, controlSplits);
                                 }
                                 LoopTransformations.unswitch(loop, controlSplits);
-                                Debug.dump(Debug.INFO_LOG_LEVEL, graph, "After unswitch %s", controlSplits);
+                                Debug.dump(Debug.DETAILED_LEVEL, graph, "After unswitch %s", controlSplits);
                                 UNSWITCHED.increment();
                                 unswitched = true;
                                 break;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopsDataTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopsDataTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -77,8 +77,8 @@
         Assert.assertEquals(1, loops.outerFirst().get(0).loop().getDepth());
         Assert.assertEquals(1, loops.outerFirst().get(1).loop().getDepth());
         Assert.assertEquals(2, loops.outerFirst().get(2).loop().getDepth());
-        Assert.assertEquals(2, loops.outerFirst().get(3).loop().getDepth());
-        Assert.assertEquals(3, loops.outerFirst().get(4).loop().getDepth());
+        Assert.assertEquals(3, loops.outerFirst().get(3).loop().getDepth());
+        Assert.assertEquals(2, loops.outerFirst().get(4).loop().getDepth());
         Assert.assertEquals(2, loops.outerFirst().get(5).loop().getDepth());
         Assert.assertEquals(3, loops.outerFirst().get(6).loop().getDepth());
         Assert.assertEquals(4, loops.outerFirst().get(7).loop().getDepth());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java	Thu Apr 06 14:31:32 2017 -0700
@@ -90,7 +90,7 @@
     @Override
     public ValueNode strideNode() {
         if (value instanceof SubNode && base.valueNode() == value.getY()) {
-            return graph().unique(new NegateNode(base.strideNode()));
+            return graph().addOrUniqueWithInputs(NegateNode.create(base.strideNode()));
         }
         return base.strideNode();
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/micro/benchmarks/ArrayListBenchmark.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/micro/benchmarks/ArrayListBenchmark.java	Thu Apr 06 14:31:32 2017 -0700
@@ -24,13 +24,14 @@
 
 import java.util.ArrayList;
 
+import org.graalvm.compiler.microbenchmarks.graal.GraalBenchmark;
 import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
 import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
 import org.openjdk.jmh.annotations.State;
 import org.openjdk.jmh.annotations.Warmup;
 
-import org.graalvm.compiler.microbenchmarks.graal.GraalBenchmark;
-
 /**
  * Benchmarks cost of ArrayList.
  */
@@ -40,7 +41,7 @@
 
     @State(Scope.Benchmark)
     public static class ThreadState {
-        ArrayList<Integer> list = new ArrayList<>(N);
+        final ArrayList<Integer> list = new ArrayList<>(N);
     }
 
     @Benchmark
@@ -61,9 +62,20 @@
         state.list.clear();
     }
 
+    @State(Scope.Benchmark)
+    public static class ClearedThreadState {
+        final ArrayList<Integer> list = new ArrayList<>(N);
+
+        // We don't want to measure the cost of list clearing
+        @Setup(Level.Invocation)
+        public void beforeInvocation() {
+            list.clear();
+        }
+    }
+
     @Benchmark
     @Warmup(iterations = 20)
-    public void addNull(ThreadState state) {
+    public void addNull(ClearedThreadState state) {
         for (int i = 0; i < N; ++i) {
             state.list.add(null);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java	Thu Apr 06 14:31:32 2017 -0700
@@ -374,13 +374,17 @@
         codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
         linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 
-        LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder, graph.getOptions());
+        LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder, getGraphOptions());
         FrameMapBuilder frameMapBuilder = request.backend.newFrameMapBuilder(registerConfig);
         lirGenRes = request.backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, request.graph, stub);
         lirGenTool = request.backend.newLIRGenerator(lirGenRes);
         nodeLirGen = request.backend.newNodeLIRBuilder(request.graph, lirGenTool);
     }
 
+    protected OptionValues getGraphOptions() {
+        return graph.getOptions();
+    }
+
     private static ControlFlowGraph deepCopy(ControlFlowGraph cfg) {
         return ControlFlowGraph.compute(cfg.graph, true, true, true, true);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/trace/TraceLSRAIntervalBuildingBench.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/trace/TraceLSRAIntervalBuildingBench.java	Thu Apr 06 14:31:32 2017 -0700
@@ -28,6 +28,7 @@
 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessAnalysisPhase;
 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo;
 import org.graalvm.compiler.lir.alloc.trace.TraceBuilderPhase;
+import org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase;
 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanLifetimeAnalysisPhase;
 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanLifetimeAnalysisPhase.Analyser;
 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase;
@@ -40,6 +41,7 @@
 import org.graalvm.compiler.lir.phases.LIRSuites;
 import org.graalvm.compiler.microbenchmarks.graal.GraalBenchmark;
 import org.graalvm.compiler.microbenchmarks.lir.GraalCompilerState;
+import org.graalvm.compiler.options.OptionValues;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Level;
 import org.openjdk.jmh.annotations.Param;
@@ -90,6 +92,11 @@
             return new LIRSuites(ls.getPreAllocationOptimizationStage(), allocationStage, ls.getPostAllocationOptimizationStage());
         }
 
+        @Override
+        protected OptionValues getGraphOptions() {
+            return new OptionValues(super.getGraphOptions(), TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints, false);
+        }
+
         @Setup(Level.Trial)
         public void setup() {
             initializeMethod();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FixedGuardNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FixedGuardNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -86,8 +86,8 @@
             /*
              * Don't allow guards with action None and reason RuntimeConstraint to float. In cases
              * where 2 guards are testing equivalent conditions they might be lowered at the same
-             * location. If the guard with the None action is lowered before the the other guard
-             * then the code will be stuck repeatedly deoptimizing without invalidating the code.
+             * location. If the guard with the None action is lowered before the other guard then
+             * the code will be stuck repeatedly deoptimizing without invalidating the code.
              * Conditional elimination will eliminate the guard if it's truly redundant in this
              * case.
              */
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Thu Apr 06 14:31:32 2017 -0700
@@ -105,7 +105,7 @@
      */
     @OptionalInput NodeInputList<ValueNode> values;
 
-    @OptionalInput(Association) NodeInputList<MonitorIdNode> monitorIds;
+    @Input(Association) NodeInputList<MonitorIdNode> monitorIds;
 
     @OptionalInput(State) NodeInputList<EscapeObjectState> virtualObjectMappings;
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -37,8 +37,8 @@
 import java.util.SortedMap;
 import java.util.TreeMap;
 
+import org.graalvm.compiler.core.common.Fields;
 import org.graalvm.compiler.core.common.PermanentBailoutException;
-import org.graalvm.compiler.core.common.Fields;
 import org.graalvm.compiler.core.common.util.TypeReader;
 import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader;
 import org.graalvm.compiler.debug.Debug;
@@ -61,9 +61,9 @@
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin.LoopExplosionKind;
-import org.graalvm.util.Equivalence;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.EconomicSet;
+import org.graalvm.util.Equivalence;
 
 import jdk.vm.ci.code.Architecture;
 import jdk.vm.ci.meta.DeoptimizationAction;
@@ -85,8 +85,6 @@
     protected class MethodScope {
         /** The loop that contains the call. Only non-null during method inlining. */
         public final LoopScope callerLoopScope;
-        /** The target graph where decoded nodes are added to. */
-        public final StructuredGraph graph;
         /**
          * Mark for nodes that were present before the decoding of this method started. Note that
          * nodes that were decoded after the mark can still be part of an outer method, since
@@ -95,20 +93,19 @@
         public final Graph.Mark methodStartMark;
         /** The encode graph that is decoded. */
         public final EncodedGraph encodedGraph;
+        /** The highest node order id that a fixed node has in the EncodedGraph. */
+        public final int maxFixedNodeOrderId;
         /** Access to the encoded graph. */
         public final TypeReader reader;
         /** The kind of loop explosion to be performed during decoding. */
         public final LoopExplosionKind loopExplosion;
-        /** A list of tasks to run before the method scope is closed. */
-        public final List<Runnable> cleanupTasks;
 
         /** All return nodes encountered during decoding. */
-        public final List<ReturnNode> returnNodes;
-        /** The exception unwind node encountered during decoding, or null. */
-        public final List<UnwindNode> unwindNodes;
+        public final List<ControlSinkNode> returnAndUnwindNodes;
 
         /** All merges created during loop explosion. */
         public final EconomicSet<Node> loopExplosionMerges;
+
         /**
          * The start of explosion, and the merge point for when irreducible loops are detected. Only
          * used when {@link MethodScope#loopExplosion} is {@link LoopExplosionKind#MERGE_EXPLODE}.
@@ -117,16 +114,14 @@
 
         protected MethodScope(LoopScope callerLoopScope, StructuredGraph graph, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) {
             this.callerLoopScope = callerLoopScope;
-            this.graph = graph;
             this.methodStartMark = graph.getMark();
             this.encodedGraph = encodedGraph;
             this.loopExplosion = loopExplosion;
-            this.cleanupTasks = new ArrayList<>(2);
-            this.returnNodes = new ArrayList<>(1);
-            this.unwindNodes = new ArrayList<>(0);
+            this.returnAndUnwindNodes = new ArrayList<>(2);
 
             if (encodedGraph != null) {
                 reader = UnsafeArrayTypeReader.create(encodedGraph.getEncoding(), encodedGraph.getStartOffset(), architecture.supportsUnalignedMemoryAccess());
+                maxFixedNodeOrderId = reader.getUVInt();
                 if (encodedGraph.nodeStartOffsets == null) {
                     int nodeCount = reader.getUVInt();
                     int[] nodeStartOffsets = new int[nodeCount];
@@ -137,6 +132,7 @@
                 }
             } else {
                 reader = null;
+                maxFixedNodeOrderId = 0;
             }
 
             if (loopExplosion != LoopExplosionKind.NONE) {
@@ -145,6 +141,10 @@
                 loopExplosionMerges = null;
             }
         }
+
+        public boolean isInlinedMethod() {
+            return false;
+        }
     }
 
     /** Decoding state maintained for each loop in the encoded graph. */
@@ -175,23 +175,24 @@
         public final Node[] createdNodes;
         /**
          * Nodes that have been created in outer loop scopes and existed before starting to process
-         * this loop, indexed by the orderId.
+         * this loop, indexed by the orderId. Only used when {@link MethodScope#loopExplosion} is
+         * not {@link LoopExplosionKind#NONE}.
          */
         public final Node[] initialCreatedNodes;
 
         protected LoopScope(MethodScope methodScope) {
             this.methodScope = methodScope;
             this.outer = null;
-            this.nextIterations = methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN ? new ArrayDeque<>() : null;
+            this.nextIterations = methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN ? new ArrayDeque<>(2) : null;
             this.loopDepth = 0;
             this.loopIteration = 0;
             this.iterationStates = null;
             this.loopBeginOrderId = -1;
 
             int nodeCount = methodScope.encodedGraph.nodeStartOffsets.length;
-            this.nodesToProcess = new BitSet(nodeCount);
-            this.initialCreatedNodes = new Node[nodeCount];
+            this.nodesToProcess = new BitSet(methodScope.maxFixedNodeOrderId);
             this.createdNodes = new Node[nodeCount];
+            this.initialCreatedNodes = null;
         }
 
         protected LoopScope(MethodScope methodScope, LoopScope outer, int loopDepth, int loopIteration, int loopBeginOrderId, Node[] initialCreatedNodes, Node[] createdNodes,
@@ -203,9 +204,9 @@
             this.nextIterations = nextIterations;
             this.iterationStates = iterationStates;
             this.loopBeginOrderId = loopBeginOrderId;
-            this.nodesToProcess = new BitSet(initialCreatedNodes.length);
+            this.nodesToProcess = new BitSet(methodScope.maxFixedNodeOrderId);
             this.initialCreatedNodes = initialCreatedNodes;
-            this.createdNodes = Arrays.copyOf(createdNodes, createdNodes.length);
+            this.createdNodes = createdNodes;
         }
 
         @Override
@@ -338,18 +339,23 @@
     }
 
     protected final Architecture architecture;
+    /** The target graph where decoded nodes are added to. */
+    protected final StructuredGraph graph;
+    private final EconomicMap<NodeClass<?>, ArrayDeque<Node>> reusableFloatingNodes;
 
-    public GraphDecoder(Architecture architecture) {
+    public GraphDecoder(Architecture architecture, StructuredGraph graph) {
         this.architecture = architecture;
+        this.graph = graph;
+        reusableFloatingNodes = EconomicMap.create(Equivalence.IDENTITY);
     }
 
     @SuppressWarnings("try")
-    public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) {
+    public final void decode(EncodedGraph encodedGraph) {
         try (Debug.Scope scope = Debug.scope("GraphDecoder", graph)) {
             MethodScope methodScope = new MethodScope(null, graph, encodedGraph, LoopExplosionKind.NONE);
             decode(createInitialLoopScope(methodScope, null));
             cleanupGraph(methodScope);
-            assert methodScope.graph.verify();
+            assert graph.verify();
         } catch (Throwable ex) {
             Debug.handle(ex);
         }
@@ -370,14 +376,10 @@
             startNode.setNext(firstNode);
             loopScope.nodesToProcess.set(GraphEncoder.FIRST_NODE_ORDER_ID);
         } else {
-            firstNode = methodScope.graph.start();
+            firstNode = graph.start();
             registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, firstNode, false, false);
             loopScope.nodesToProcess.set(GraphEncoder.START_NODE_ORDER_ID);
         }
-
-        if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) {
-            methodScope.cleanupTasks.add(new LoopDetector(methodScope, startNode));
-        }
         return loopScope;
     }
 
@@ -411,18 +413,26 @@
             }
 
             /*
-             * Finished with an inlined method. Perform all registered end-of-method cleanup tasks
-             * and continue with loop that contained the call.
+             * Finished with an inlined method. Perform end-of-method cleanup tasks.
              */
-            for (Runnable task : methodScope.cleanupTasks) {
-                task.run();
+            if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) {
+                LoopDetector loopDetector = new LoopDetector(graph, methodScope);
+                loopDetector.run();
             }
+            if (methodScope.isInlinedMethod()) {
+                finishInlining(methodScope);
+            }
+
+            /* continue with the caller */
             loopScope = methodScope.callerLoopScope;
         }
     }
 
+    protected void finishInlining(@SuppressWarnings("unused") MethodScope inlineScope) {
+    }
+
     private static void propagateCreatedNodes(LoopScope loopScope) {
-        if (loopScope.outer == null) {
+        if (loopScope.outer == null || loopScope.createdNodes != loopScope.outer.createdNodes) {
             return;
         }
 
@@ -474,7 +484,7 @@
                 LoopScope outerScope = loopScope.outer;
                 int nextIterationNumber = outerScope.nextIterations.isEmpty() ? outerScope.loopIteration + 1 : outerScope.nextIterations.getLast().loopIteration + 1;
                 successorAddScope = new LoopScope(methodScope, outerScope.outer, outerScope.loopDepth, nextIterationNumber, outerScope.loopBeginOrderId, outerScope.initialCreatedNodes,
-                                loopScope.initialCreatedNodes, outerScope.nextIterations, outerScope.iterationStates);
+                                Arrays.copyOf(loopScope.initialCreatedNodes, loopScope.initialCreatedNodes.length), outerScope.nextIterations, outerScope.iterationStates);
                 checkLoopExplosionIteration(methodScope, successorAddScope);
 
                 /*
@@ -496,8 +506,8 @@
         methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
         int typeId = methodScope.reader.getUVInt();
         assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId];
+        makeFixedNodeInputs(methodScope, loopScope, node);
         readProperties(methodScope, node);
-        makeInputNodes(methodScope, loopScope, node, true);
         makeSuccessorStubs(methodScope, successorAddScope, node, updatePredecessors);
 
         LoopScope resultScope = loopScope;
@@ -533,13 +543,16 @@
                 if (merge instanceof LoopBeginNode) {
                     assert phiNodeScope == phiInputScope && phiNodeScope == loopScope;
                     resultScope = new LoopScope(methodScope, loopScope, loopScope.loopDepth + 1, 0, mergeOrderId,
-                                    Arrays.copyOf(loopScope.createdNodes, loopScope.createdNodes.length), loopScope.createdNodes, //
-                                    methodScope.loopExplosion != LoopExplosionKind.NONE ? new ArrayDeque<>() : null, //
+                                    methodScope.loopExplosion != LoopExplosionKind.NONE ? Arrays.copyOf(loopScope.createdNodes, loopScope.createdNodes.length) : null,
+                                    methodScope.loopExplosion != LoopExplosionKind.NONE ? Arrays.copyOf(loopScope.createdNodes, loopScope.createdNodes.length) : loopScope.createdNodes, //
+                                    methodScope.loopExplosion != LoopExplosionKind.NONE ? new ArrayDeque<>(2) : null, //
                                     methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE ? EconomicMap.create(Equivalence.DEFAULT) : null);
                     phiInputScope = resultScope;
                     phiNodeScope = resultScope;
 
-                    registerNode(loopScope, mergeOrderId, null, true, true);
+                    if (methodScope.loopExplosion != LoopExplosionKind.NONE) {
+                        registerNode(loopScope, mergeOrderId, null, true, true);
+                    }
                     loopScope.nodesToProcess.clear(mergeOrderId);
                     resultScope.nodesToProcess.set(mergeOrderId);
                 }
@@ -551,11 +564,8 @@
             InvokeData invokeData = readInvokeData(methodScope, nodeOrderId, (Invoke) node);
             resultScope = handleInvoke(methodScope, loopScope, invokeData);
 
-        } else if (node instanceof ReturnNode) {
-            methodScope.returnNodes.add((ReturnNode) node);
-        } else if (node instanceof UnwindNode) {
-            methodScope.unwindNodes.add((UnwindNode) node);
-
+        } else if (node instanceof ReturnNode || node instanceof UnwindNode) {
+            methodScope.returnAndUnwindNodes.add((ControlSinkNode) node);
         } else {
             handleFixedNode(methodScope, loopScope, nodeOrderId, node);
         }
@@ -642,7 +652,7 @@
             }
         }
 
-        MergeNode merge = methodScope.graph.add(new MergeNode());
+        MergeNode merge = graph.add(new MergeNode());
         methodScope.loopExplosionMerges.add(merge);
 
         if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) {
@@ -655,11 +665,11 @@
 
             List<ValueNode> newFrameStateValues = new ArrayList<>();
             for (ValueNode frameStateValue : frameState.values) {
-                if (frameStateValue == null || frameStateValue.isConstant() || !methodScope.graph.isNew(methodScope.methodStartMark, frameStateValue)) {
+                if (frameStateValue == null || frameStateValue.isConstant() || !graph.isNew(methodScope.methodStartMark, frameStateValue)) {
                     newFrameStateValues.add(frameStateValue);
 
                 } else {
-                    ProxyPlaceholder newFrameStateValue = methodScope.graph.unique(new ProxyPlaceholder(frameStateValue, merge));
+                    ProxyPlaceholder newFrameStateValue = graph.unique(new ProxyPlaceholder(frameStateValue, merge));
                     newFrameStateValues.add(newFrameStateValue);
 
                     /*
@@ -670,14 +680,19 @@
                         if (loopScope.createdNodes[i] == frameStateValue) {
                             loopScope.createdNodes[i] = newFrameStateValue;
                         }
-                        if (loopScope.initialCreatedNodes[i] == frameStateValue) {
-                            loopScope.initialCreatedNodes[i] = newFrameStateValue;
+                    }
+
+                    if (loopScope.initialCreatedNodes != null) {
+                        for (int i = 0; i < loopScope.initialCreatedNodes.length; i++) {
+                            if (loopScope.initialCreatedNodes[i] == frameStateValue) {
+                                loopScope.initialCreatedNodes[i] = newFrameStateValue;
+                            }
                         }
                     }
                 }
             }
 
-            FrameState newFrameState = methodScope.graph.add(new FrameState(frameState.outerFrameState(), frameState.getCode(), frameState.bci, newFrameStateValues, frameState.localsSize(),
+            FrameState newFrameState = graph.add(new FrameState(frameState.outerFrameState(), frameState.getCode(), frameState.bci, newFrameStateValues, frameState.localsSize(),
                             frameState.stackSize(), frameState.rethrowException(), frameState.duringCall(), frameState.monitorIds(), frameState.virtualObjectMappings()));
 
             frameState.replaceAtUsagesAndDelete(newFrameState);
@@ -708,7 +723,7 @@
     }
 
     protected FixedNode handleLoopExplosionEnd(MethodScope methodScope, LoopScope loopScope, LoopEndNode loopEnd) {
-        EndNode replacementNode = methodScope.graph.add(new EndNode());
+        EndNode replacementNode = graph.add(new EndNode());
         loopEnd.replaceAtPredecessor(replacementNode);
         loopEnd.safeDelete();
 
@@ -716,7 +731,7 @@
         if (methodScope.loopExplosion != LoopExplosionKind.FULL_UNROLL || loopScope.nextIterations.isEmpty()) {
             int nextIterationNumber = loopScope.nextIterations.isEmpty() ? loopScope.loopIteration + 1 : loopScope.nextIterations.getLast().loopIteration + 1;
             LoopScope nextIterationScope = new LoopScope(methodScope, loopScope.outer, loopScope.loopDepth, nextIterationNumber, loopScope.loopBeginOrderId, loopScope.initialCreatedNodes,
-                            loopScope.initialCreatedNodes, loopScope.nextIterations, loopScope.iterationStates);
+                            Arrays.copyOf(loopScope.initialCreatedNodes, loopScope.initialCreatedNodes.length), loopScope.nextIterations, loopScope.iterationStates);
             checkLoopExplosionIteration(methodScope, nextIterationScope);
             loopScope.nextIterations.addLast(nextIterationScope);
             registerNode(nextIterationScope, loopScope.loopBeginOrderId, null, true, true);
@@ -749,7 +764,9 @@
              * The ProxyNode transports a value from the loop to the outer scope. We therefore
              * register it in the outer scope.
              */
-            registerNode(loopScope.outer, proxyOrderId, proxy, false, false);
+            if (loopScope.outer.createdNodes != loopScope.createdNodes) {
+                registerNode(loopScope.outer, proxyOrderId, proxy, false, false);
+            }
         }
     }
 
@@ -757,7 +774,7 @@
         assert loopExit.stateAfter() == null;
         int stateAfterOrderId = readOrderId(methodScope);
 
-        BeginNode begin = methodScope.graph.add(new BeginNode());
+        BeginNode begin = graph.add(new BeginNode());
 
         FixedNode loopExitSuccessor = loopExit.next();
         loopExit.replaceAtPredecessor(begin);
@@ -768,14 +785,14 @@
              * This exit might end up as a loop exit of a loop detected after partial evaluation. We
              * need to be able to create a FrameState and the necessary proxy nodes in this case.
              */
-            loopExitPlaceholder = methodScope.graph.add(new MergeNode());
+            loopExitPlaceholder = graph.add(new MergeNode());
             methodScope.loopExplosionMerges.add(loopExitPlaceholder);
 
-            EndNode end = methodScope.graph.add(new EndNode());
+            EndNode end = graph.add(new EndNode());
             begin.setNext(end);
             loopExitPlaceholder.addForwardEnd(end);
 
-            begin = methodScope.graph.add(new BeginNode());
+            begin = graph.add(new BeginNode());
             loopExitPlaceholder.setNext(begin);
         }
 
@@ -793,10 +810,10 @@
 
         } else if (existingExit instanceof BeginNode) {
             /* Second loop iteration that exits. Create the merge. */
-            merge = methodScope.graph.add(new MergeNode());
+            merge = graph.add(new MergeNode());
             registerNode(outerScope, loopExitOrderId, merge, true, false);
             /* Add the first iteration. */
-            EndNode firstEnd = methodScope.graph.add(new EndNode());
+            EndNode firstEnd = graph.add(new EndNode());
             ((BeginNode) existingExit).setNext(firstEnd);
             merge.addForwardEnd(firstEnd);
             merge.setNext(loopExitSuccessor);
@@ -807,7 +824,7 @@
         }
 
         if (merge != null) {
-            EndNode end = methodScope.graph.add(new EndNode());
+            EndNode end = graph.add(new EndNode());
             begin.setNext(end);
             merge.addForwardEnd(end);
         }
@@ -826,7 +843,7 @@
 
             if (loopExitPlaceholder != null) {
                 if (!phiInput.isConstant()) {
-                    phiInput = methodScope.graph.unique(new ProxyPlaceholder(phiInput, loopExitPlaceholder));
+                    phiInput = graph.unique(new ProxyPlaceholder(phiInput, loopExitPlaceholder));
                 }
                 registerNode(loopScope, proxyOrderId, phiInput, true, false);
             }
@@ -843,7 +860,14 @@
 
             } else if (!merge.isPhiAtMerge(existing)) {
                 /* Now we have two different values, so we need to create a phi node. */
-                PhiNode phi = methodScope.graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(), merge));
+                PhiNode phi;
+                if (proxy instanceof ValueProxyNode) {
+                    phi = graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(), merge));
+                } else if (proxy instanceof GuardProxyNode) {
+                    phi = graph.addWithoutUnique(new GuardPhiNode(merge));
+                } else {
+                    throw GraalError.shouldNotReachHere();
+                }
                 /* Add the inputs from all previous exits. */
                 for (int j = 0; j < merge.phiPredecessorCount() - 1; j++) {
                     phi.addInput(existing);
@@ -954,12 +978,6 @@
         return false;
     }
 
-    protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) {
-        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
-        NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
-        return nodeClass.allocateInstance();
-    }
-
     protected void readProperties(MethodScope methodScope, Node node) {
         node.setNodeSourcePosition((NodeSourcePosition) readObject(methodScope));
         Fields fields = node.getNodeClass().getData();
@@ -980,7 +998,7 @@
      * are created on demand (recursively since they can themselves reference not yet created
      * nodes).
      */
-    protected void makeInputNodes(MethodScope methodScope, LoopScope loopScope, Node node, boolean updateUsages) {
+    protected void makeFixedNodeInputs(MethodScope methodScope, LoopScope loopScope, Node node) {
         Edges edges = node.getNodeClass().getInputEdges();
         for (int index = 0; index < edges.getDirectCount(); index++) {
             if (skipDirectEdge(node, edges, index)) {
@@ -989,25 +1007,60 @@
             int orderId = readOrderId(methodScope);
             Node value = ensureNodeCreated(methodScope, loopScope, orderId);
             edges.initializeNode(node, index, value);
-            if (updateUsages && value != null && !value.isDeleted()) {
+            if (value != null && !value.isDeleted()) {
                 edges.update(node, null, value);
 
             }
         }
-        for (int index = edges.getDirectCount(); index < edges.getCount(); index++) {
-            if (skipIndirectEdge(node, edges, index, true)) {
-                continue;
+
+        if (node instanceof AbstractMergeNode) {
+            /* The ends of merge nodes are filled manually when the ends are processed. */
+            assert edges.getCount() - edges.getDirectCount() == 1 : "MergeNode has one variable size input (the ends)";
+            assert Edges.getNodeList(node, edges.getOffsets(), edges.getDirectCount()) != null : "Input list must have been already created";
+        } else {
+            for (int index = edges.getDirectCount(); index < edges.getCount(); index++) {
+                int size = methodScope.reader.getSVInt();
+                if (size != -1) {
+                    NodeList<Node> nodeList = new NodeInputList<>(node, size);
+                    edges.initializeList(node, index, nodeList);
+                    for (int idx = 0; idx < size; idx++) {
+                        int orderId = readOrderId(methodScope);
+                        Node value = ensureNodeCreated(methodScope, loopScope, orderId);
+                        nodeList.initialize(idx, value);
+                        if (value != null && !value.isDeleted()) {
+                            edges.update(node, null, value);
+                        }
+                    }
+                }
             }
-            int size = methodScope.reader.getSVInt();
-            if (size != -1) {
-                NodeList<Node> nodeList = new NodeInputList<>(node, size);
-                edges.initializeList(node, index, nodeList);
-                for (int idx = 0; idx < size; idx++) {
-                    int orderId = readOrderId(methodScope);
-                    Node value = ensureNodeCreated(methodScope, loopScope, orderId);
-                    nodeList.initialize(idx, value);
-                    if (updateUsages && value != null && !value.isDeleted()) {
-                        edges.update(node, null, value);
+        }
+    }
+
+    protected void makeFloatingNodeInputs(MethodScope methodScope, LoopScope loopScope, Node node) {
+        Edges edges = node.getNodeClass().getInputEdges();
+        if (node instanceof PhiNode) {
+            /*
+             * The inputs of phi functions are filled manually when the end nodes are processed.
+             * However, the values must not be null, so initialize them with an empty list.
+             */
+            assert edges.getDirectCount() == 1 : "PhiNode has one direct input (the MergeNode)";
+            assert edges.getCount() - edges.getDirectCount() == 1 : "PhiNode has one variable size input (the values)";
+            edges.initializeList(node, edges.getDirectCount(), new NodeInputList<>(node));
+        } else {
+            for (int index = 0; index < edges.getDirectCount(); index++) {
+                int orderId = readOrderId(methodScope);
+                Node value = ensureNodeCreated(methodScope, loopScope, orderId);
+                edges.initializeNode(node, index, value);
+            }
+            for (int index = edges.getDirectCount(); index < edges.getCount(); index++) {
+                int size = methodScope.reader.getSVInt();
+                if (size != -1) {
+                    NodeList<Node> nodeList = new NodeInputList<>(node, size);
+                    edges.initializeList(node, index, nodeList);
+                    for (int idx = 0; idx < size; idx++) {
+                        int orderId = readOrderId(methodScope);
+                        Node value = ensureNodeCreated(methodScope, loopScope, orderId);
+                        nodeList.initialize(idx, value);
                     }
                 }
             }
@@ -1024,31 +1077,34 @@
         }
 
         node = decodeFloatingNode(methodScope, loopScope, nodeOrderId);
-
         if (node instanceof ProxyNode || node instanceof PhiNode) {
             /*
              * We need these nodes as they were in the original graph, without any canonicalization
              * or value numbering.
              */
-            node = methodScope.graph.addWithoutUnique(node);
+            node = graph.addWithoutUnique(node);
         } else {
             /* Allow subclasses to canonicalize and intercept nodes. */
-            node = handleFloatingNodeBeforeAdd(methodScope, loopScope, node);
-            if (!node.isAlive()) {
-                node = addFloatingNode(methodScope, node);
+            Node newNode = handleFloatingNodeBeforeAdd(methodScope, loopScope, node);
+            if (newNode != node) {
+                releaseFloatingNode(node);
             }
-            node = handleFloatingNodeAfterAdd(methodScope, loopScope, node);
+
+            if (!newNode.isAlive()) {
+                newNode = addFloatingNode(methodScope, newNode);
+            }
+            node = handleFloatingNodeAfterAdd(methodScope, loopScope, newNode);
         }
         registerNode(loopScope, nodeOrderId, node, false, false);
         return node;
     }
 
-    protected Node addFloatingNode(MethodScope methodScope, Node node) {
+    protected Node addFloatingNode(@SuppressWarnings("unused") MethodScope methodScope, Node node) {
         /*
          * We want to exactly reproduce the encoded graph. Even though nodes should be unique in the
          * encoded graph, this is not always guaranteed.
          */
-        return methodScope.graph.addWithoutUnique(node);
+        return graph.addWithoutUnique(node);
     }
 
     /**
@@ -1056,7 +1112,10 @@
      */
     protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) {
         long readerByteIndex = methodScope.reader.getByteIndex();
-        Node node = instantiateNode(methodScope, nodeOrderId);
+
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
+        NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
+        Node node = allocateFloatingNode(nodeClass);
         if (node instanceof FixedNode) {
             /*
              * This is a severe error that will lead to a corrupted graph, so it is better not to
@@ -1065,16 +1124,38 @@
             throw shouldNotReachHere("Not a floating node: " + node.getClass().getName());
         }
 
+        /* Read the inputs of the node, possibly creating them recursively. */
+        makeFloatingNodeInputs(methodScope, loopScope, node);
+
         /* Read the properties of the node. */
         readProperties(methodScope, node);
         /* There must not be any successors to read, since it is a non-fixed node. */
         assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
-        /* Read the inputs of the node, possibly creating them recursively. */
-        makeInputNodes(methodScope, loopScope, node, false);
+
         methodScope.reader.setByteIndex(readerByteIndex);
         return node;
     }
 
+    private Node allocateFloatingNode(NodeClass<?> nodeClass) {
+        ArrayDeque<? extends Node> cachedNodes = reusableFloatingNodes.get(nodeClass);
+        if (cachedNodes != null) {
+            Node node = cachedNodes.poll();
+            if (node != null) {
+                return node;
+            }
+        }
+        return nodeClass.allocateInstance();
+    }
+
+    private void releaseFloatingNode(Node node) {
+        ArrayDeque<Node> cachedNodes = reusableFloatingNodes.get(node.getNodeClass());
+        if (cachedNodes == null) {
+            cachedNodes = new ArrayDeque<>(2);
+            reusableFloatingNodes.put(node.getNodeClass(), cachedNodes);
+        }
+        cachedNodes.push(node);
+    }
+
     /**
      * Hook for subclasses to process a non-fixed node before it is added to the graph.
      *
@@ -1121,9 +1202,6 @@
             }
         }
         for (int index = edges.getDirectCount(); index < edges.getCount(); index++) {
-            if (skipIndirectEdge(node, edges, index, true)) {
-                continue;
-            }
             int size = methodScope.reader.getSVInt();
             if (size != -1) {
                 NodeList<Node> nodeList = new NodeSuccessorList<>(node, size);
@@ -1150,7 +1228,9 @@
         }
 
         long readerByteIndex = methodScope.reader.getByteIndex();
-        node = (FixedNode) methodScope.graph.add(instantiateNode(methodScope, nodeOrderId));
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
+        NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
+        node = (FixedNode) graph.add(nodeClass.allocateInstance());
         /* Properties and edges are not filled yet, the node remains uninitialized. */
         methodScope.reader.setByteIndex(readerByteIndex);
 
@@ -1174,12 +1254,6 @@
                     return true;
                 }
             }
-        } else if (node instanceof PhiNode) {
-            /* The inputs of phi functions are filled manually when the end nodes are processed. */
-            assert edges.type() == Edges.Type.Inputs;
-            assert index == edges.getDirectCount() - 1 : "PhiNode has one direct input (the MergeNode)";
-            return true;
-
         } else if (node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class) {
             /* The stateAfter of the loop exit is filled manually. */
             return true;
@@ -1188,29 +1262,6 @@
         return false;
     }
 
-    protected static boolean skipIndirectEdge(Node node, Edges edges, int index, boolean decode) {
-        assert !(node instanceof Invoke);
-        assert !(node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class);
-        if (node instanceof AbstractMergeNode && edges.type() == Edges.Type.Inputs) {
-            /* The ends of merge nodes are filled manually when the ends are processed. */
-            assert index == edges.getCount() - 1 : "MergeNode has one variable size input (the ends)";
-            assert Edges.getNodeList(node, edges.getOffsets(), index) != null : "Input list must have been already created";
-            return true;
-
-        } else if (node instanceof PhiNode) {
-            /* The inputs of phi functions are filled manually when the end nodes are processed. */
-            assert edges.type() == Edges.Type.Inputs;
-            assert index == edges.getCount() - 1 : "PhiNode has one variable size input (the values)";
-            if (decode) {
-                /* The values must not be null, so initialize with an empty list. */
-                edges.initializeList(node, index, new NodeInputList<>(node));
-            }
-            return true;
-
-        }
-        return false;
-    }
-
     protected Node lookupNode(LoopScope loopScope, int nodeOrderId) {
         return loopScope.createdNodes[nodeOrderId];
     }
@@ -1236,11 +1287,11 @@
      * @param methodScope The current method.
      */
     protected void cleanupGraph(MethodScope methodScope) {
-        assert verifyEdges(methodScope);
+        assert verifyEdges();
     }
 
-    protected boolean verifyEdges(MethodScope methodScope) {
-        for (Node node : methodScope.graph.getNodes()) {
+    protected boolean verifyEdges() {
+        for (Node node : graph.getNodes()) {
             assert node.isAlive();
             for (Node i : node.inputs()) {
                 assert i.isAlive();
@@ -1278,7 +1329,7 @@
          * The ends, i.e., the source of backward branches. The {@link EndNode#successors successor}
          * is the {@link #header loop header}.
          */
-        List<EndNode> ends = new ArrayList<>();
+        List<EndNode> ends = new ArrayList<>(2);
         /**
          * Exits of the loop. The successor is a {@link MergeNode} marked in
          * {@link MethodScope#loopExplosionMerges}.
@@ -1291,20 +1342,20 @@
         boolean irreducible;
     }
 
+    private final StructuredGraph graph;
     private final MethodScope methodScope;
-    private final FixedNode startInstruction;
 
     private Loop irreducibleLoopHandler;
     private IntegerSwitchNode irreducibleLoopSwitch;
 
-    protected LoopDetector(MethodScope methodScope, FixedNode startInstruction) {
+    protected LoopDetector(StructuredGraph graph, MethodScope methodScope) {
+        this.graph = graph;
         this.methodScope = methodScope;
-        this.startInstruction = startInstruction;
     }
 
     @Override
     public void run() {
-        Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "Before loop detection");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "Before loop detection");
 
         List<Loop> orderedLoops = findLoops();
         assert orderedLoops.get(orderedLoops.size() - 1) == irreducibleLoopHandler : "outermost loop must be the last element in the list";
@@ -1327,11 +1378,11 @@
             } else {
                 insertLoopNodes(loop);
             }
-            Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "After handling of loop %s", loop.header);
+            Debug.dump(Debug.DETAILED_LEVEL, graph, "After handling of loop %s", loop.header);
         }
 
         logIrreducibleLoops();
-        Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "After loop detection");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "After loop detection");
     }
 
     private List<Loop> findLoops() {
@@ -1346,11 +1397,11 @@
          */
         irreducibleLoopHandler = findOrCreateLoop(unorderedLoops, methodScope.loopExplosionHead);
 
-        NodeBitMap visited = methodScope.graph.createNodeBitMap();
-        NodeBitMap active = methodScope.graph.createNodeBitMap();
+        NodeBitMap visited = graph.createNodeBitMap();
+        NodeBitMap active = graph.createNodeBitMap();
         Deque<Node> stack = new ArrayDeque<>();
-        visited.mark(startInstruction);
-        stack.push(startInstruction);
+        visited.mark(methodScope.loopExplosionHead);
+        stack.push(methodScope.loopExplosionHead);
 
         while (!stack.isEmpty()) {
             Node current = stack.peek();
@@ -1421,7 +1472,7 @@
          */
 
         List<Node> possibleExits = new ArrayList<>();
-        NodeBitMap visited = methodScope.graph.createNodeBitMap();
+        NodeBitMap visited = graph.createNodeBitMap();
         Deque<Node> stack = new ArrayDeque<>();
         for (EndNode loopEnd : loop.ends) {
             stack.push(loopEnd);
@@ -1433,7 +1484,7 @@
             if (current == loop.header) {
                 continue;
             }
-            if (!methodScope.graph.isNew(methodScope.methodStartMark, current)) {
+            if (!graph.isNew(methodScope.methodStartMark, current)) {
                 /*
                  * The current node is before the method that contains the exploded loop. The loop
                  * must have a second entry point, i.e., it is an irreducible loop.
@@ -1498,7 +1549,6 @@
          * necessary into a loop because it computes loop information based on bytecodes, before the
          * actual parsing.
          */
-
         for (Node succ : possibleExits) {
             if (!visited.contains(succ)) {
                 stack.push(succ);
@@ -1541,8 +1591,8 @@
         FrameState stateAfter = merge.stateAfter().duplicate();
         FixedNode afterMerge = merge.next();
         merge.setNext(null);
-        EndNode preLoopEnd = methodScope.graph.add(new EndNode());
-        LoopBeginNode loopBegin = methodScope.graph.add(new LoopBeginNode());
+        EndNode preLoopEnd = graph.add(new EndNode());
+        LoopBeginNode loopBegin = graph.add(new LoopBeginNode());
 
         merge.setNext(preLoopEnd);
         /* Add the single non-loop predecessor of the loop header. */
@@ -1559,7 +1609,7 @@
         List<PhiNode> loopBeginPhis = new ArrayList<>(mergePhis.size());
         for (int i = 0; i < mergePhis.size(); i++) {
             PhiNode mergePhi = mergePhis.get(i);
-            PhiNode loopBeginPhi = methodScope.graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(), loopBegin));
+            PhiNode loopBeginPhi = graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(), loopBegin));
             mergePhi.replaceAtUsages(loopBeginPhi);
             /*
              * The first input of the new phi function is the original phi function, for the one
@@ -1577,7 +1627,7 @@
             }
 
             merge.removeEnd(endNode);
-            LoopEndNode loopEnd = methodScope.graph.add(new LoopEndNode(loopBegin));
+            LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
             endNode.replaceAndDelete(loopEnd);
         }
 
@@ -1589,7 +1639,7 @@
             AbstractMergeNode loopExplosionMerge = exit.merge();
             assert methodScope.loopExplosionMerges.contains(loopExplosionMerge);
 
-            LoopExitNode loopExit = methodScope.graph.add(new LoopExitNode(loopBegin));
+            LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
             exit.replaceAtPredecessor(loopExit);
             loopExit.setNext(exit);
             assignLoopExitState(loopExit, loopExplosionMerge, exit);
@@ -1630,7 +1680,7 @@
                 realValue = ProxyPlaceholder.unwrap(value);
             }
 
-            if (realValue == null || realValue.isConstant() || loopBeginValues.contains(realValue) || !methodScope.graph.isNew(methodScope.methodStartMark, realValue)) {
+            if (realValue == null || realValue.isConstant() || loopBeginValues.contains(realValue) || !graph.isNew(methodScope.methodStartMark, realValue)) {
                 newValues.add(realValue);
             } else {
                 /*
@@ -1641,7 +1691,7 @@
                                 "Value flowing out of loop, but we are not prepared to insert a ProxyNode");
 
                 ProxyPlaceholder proxyPlaceholder = (ProxyPlaceholder) value;
-                ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit, methodScope.graph);
+                ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit, graph);
                 proxyPlaceholder.setValue(proxy);
                 newValues.add(proxy);
             }
@@ -1651,7 +1701,7 @@
                         oldState.duringCall(), oldState.monitorIds(), oldState.virtualObjectMappings());
 
         assert loopExit.stateAfter() == null;
-        loopExit.setStateAfter(methodScope.graph.add(newState));
+        loopExit.setStateAfter(graph.add(newState));
     }
 
     /**
@@ -1722,7 +1772,7 @@
             assert irreducibleLoopHandler.header.phis().isEmpty();
 
             /* The new phi function for the loop variable. */
-            loopVariablePhi = methodScope.graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp().unrestricted(), irreducibleLoopHandler.header));
+            loopVariablePhi = graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp().unrestricted(), irreducibleLoopHandler.header));
             for (int i = 0; i < irreducibleLoopHandler.header.phiPredecessorCount(); i++) {
                 loopVariablePhi.addInput(explosionHeadValue);
             }
@@ -1732,7 +1782,7 @@
              * to the old FrameState: the loop variable is replaced with the phi function.
              */
             FrameState oldFrameState = explosionHeadState;
-            List<ValueNode> newFrameStateValues = new ArrayList<>();
+            List<ValueNode> newFrameStateValues = new ArrayList<>(explosionHeadValues.size());
             for (int i = 0; i < explosionHeadValues.size(); i++) {
                 if (i == loopVariableIndex) {
                     newFrameStateValues.add(loopVariablePhi);
@@ -1740,7 +1790,8 @@
                     newFrameStateValues.add(explosionHeadValues.get(i));
                 }
             }
-            FrameState newFrameState = methodScope.graph.add(
+
+            FrameState newFrameState = graph.add(
                             new FrameState(oldFrameState.outerFrameState(), oldFrameState.getCode(), oldFrameState.bci, newFrameStateValues, oldFrameState.localsSize(),
                                             oldFrameState.stackSize(), oldFrameState.rethrowException(), oldFrameState.duringCall(), oldFrameState.monitorIds(),
                                             oldFrameState.virtualObjectMappings()));
@@ -1752,7 +1803,7 @@
              */
             FixedNode handlerNext = irreducibleLoopHandler.header.next();
             irreducibleLoopHandler.header.setNext(null);
-            BeginNode handlerBegin = methodScope.graph.add(new BeginNode());
+            BeginNode handlerBegin = graph.add(new BeginNode());
             handlerBegin.setNext(handlerNext);
             dispatchTable.put(asInt(explosionHeadValue), handlerBegin);
 
@@ -1760,8 +1811,8 @@
              * We know that there will always be a matching key in the switch. But Graal always
              * wants a default successor, so we build a dummy block that just deoptimizes.
              */
-            unreachableDefaultSuccessor = methodScope.graph.add(new BeginNode());
-            DeoptimizeNode deopt = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode));
+            unreachableDefaultSuccessor = graph.add(new BeginNode());
+            DeoptimizeNode deopt = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode));
             unreachableDefaultSuccessor.setNext(deopt);
 
         } else {
@@ -1796,8 +1847,8 @@
 
         /* Insert our loop into the dispatch state machine. */
         assert loop.header.phis().isEmpty();
-        BeginNode dispatchBegin = methodScope.graph.add(new BeginNode());
-        EndNode dispatchEnd = methodScope.graph.add(new EndNode());
+        BeginNode dispatchBegin = graph.add(new BeginNode());
+        EndNode dispatchEnd = graph.add(new EndNode());
         dispatchBegin.setNext(dispatchEnd);
         loop.header.addForwardEnd(dispatchEnd);
         int intLoopValue = asInt(loopValue);
@@ -1813,7 +1864,7 @@
         }
 
         /* Build and insert the switch node. */
-        irreducibleLoopSwitch = methodScope.graph.add(createSwitch(loopVariablePhi, dispatchTable, unreachableDefaultSuccessor));
+        irreducibleLoopSwitch = graph.add(createSwitch(loopVariablePhi, dispatchTable, unreachableDefaultSuccessor));
         irreducibleLoopHandler.header.setNext(irreducibleLoopSwitch);
     }
 
@@ -1859,14 +1910,14 @@
     @SuppressWarnings("try")
     private void logIrreducibleLoops() {
         try (Debug.Scope s = Debug.scope("IrreducibleLoops")) {
-            if (Debug.isLogEnabled(Debug.BASIC_LOG_LEVEL) && irreducibleLoopSwitch != null) {
+            if (Debug.isLogEnabled(Debug.BASIC_LEVEL) && irreducibleLoopSwitch != null) {
                 StringBuilder msg = new StringBuilder("Inserted state machine to remove irreducible loops. Dispatching to the following states: ");
                 String sep = "";
                 for (int i = 0; i < irreducibleLoopSwitch.keyCount(); i++) {
                     msg.append(sep).append(irreducibleLoopSwitch.keyAt(i).asInt());
                     sep = ", ";
                 }
-                Debug.log(Debug.BASIC_LOG_LEVEL, "%s", msg);
+                Debug.log(Debug.BASIC_LEVEL, "%s", msg);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -67,10 +67,11 @@
  * encoding-local.
  *
  * The encoded graph has the following structure: First, all nodes and their edges are serialized.
- * The start offset of every node is then known. The raw node data is followed by a "table of
- * contents" that lists the start offset for every node.
+ * The start offset of every node is then known. The raw node data is followed by metadata, i.e.,
+ * the maximum fixed node order id and a "table of contents" that lists the start offset for every
+ * node.
  *
- * The beginning of that table of contents is the return value of {@link #encode} and stored in
+ * The beginning of this metadata is the return value of {@link #encode} and stored in
  * {@link EncodedGraph#getStartOffset()}. The order of nodes in the table of contents is the
  * {@link NodeOrder#orderIds orderId} of a node. Note that the orderId is not the regular node id
  * that every Graal graph node gets assigned. The orderId is computed and used just for encoding and
@@ -85,8 +86,8 @@
  * <pre>
  * struct Node {
  *   unsigned typeId
+ *   unsigned[] inputOrderIds
  *   signed[] properties
- *   unsigned[] inputOrderIds
  *   unsigned[] successorOrderIds
  * }
  * </pre>
@@ -168,9 +169,8 @@
      */
     public void prepare(StructuredGraph graph) {
         for (Node node : graph.getNodes()) {
-            nodeClasses.addObject(node.getNodeClass());
-
-            NodeClass<?> nodeClass = node.getNodeClass();
+            NodeClass<? extends Node> nodeClass = node.getNodeClass();
+            nodeClasses.addObject(nodeClass);
             objects.addObject(node.getNodeSourcePosition());
             for (int i = 0; i < nodeClass.getData().getCount(); i++) {
                 if (!nodeClass.getData().getType(i).isPrimitive()) {
@@ -223,8 +223,8 @@
             /* Write out the type, properties, and edges. */
             NodeClass<?> nodeClass = node.getNodeClass();
             writer.putUV(nodeClasses.getIndex(nodeClass));
+            writeEdges(node, nodeClass.getEdges(Edges.Type.Inputs), nodeOrder);
             writeProperties(node, nodeClass.getData());
-            writeEdges(node, nodeClass.getEdges(Edges.Type.Inputs), nodeOrder);
             writeEdges(node, nodeClass.getEdges(Edges.Type.Successors), nodeOrder);
 
             /* Special handling for some nodes that require additional information for decoding. */
@@ -276,18 +276,23 @@
             }
         }
 
-        /* Write out the table of contents with the start offset for all nodes. */
-        int nodeTableStart = TypeConversion.asS4(writer.getBytesWritten());
+        /*
+         * Write out the metadata (maximum fixed node order id and the table of contents with the
+         * start offset for all nodes).
+         */
+        int metadataStart = TypeConversion.asS4(writer.getBytesWritten());
+        writer.putUV(nodeOrder.maxFixedNodeOrderId);
         writer.putUV(nodeCount);
         for (int i = 0; i < nodeCount; i++) {
             assert i == NULL_ORDER_ID || i == START_NODE_ORDER_ID || nodeStartOffsets[i] > 0;
-            writer.putUV(nodeTableStart - nodeStartOffsets[i]);
+            writer.putUV(metadataStart - nodeStartOffsets[i]);
         }
 
         /* Check that the decoding of the encode graph is the same as the input. */
-        assert verifyEncoding(graph, new EncodedGraph(getEncoding(), nodeTableStart, getObjects(), getNodeClasses(), graph.getAssumptions(), graph.getMethods()), architecture);
+        assert verifyEncoding(graph, new EncodedGraph(getEncoding(), metadataStart, getObjects(), getNodeClasses(), graph.getAssumptions(), graph.getMethods()),
+                        architecture);
 
-        return nodeTableStart;
+        return metadataStart;
     }
 
     public byte[] getEncoding() {
@@ -297,6 +302,7 @@
     static class NodeOrder {
         protected final NodeMap<Integer> orderIds;
         protected int nextOrderId;
+        protected int maxFixedNodeOrderId;
 
         NodeOrder(StructuredGraph graph) {
             this.orderIds = new NodeMap<>(graph);
@@ -337,6 +343,7 @@
                 }
             } while (current != null);
 
+            maxFixedNodeOrderId = nextOrderId - 1;
             for (Node node : graph.getNodes()) {
                 assert (node instanceof FixedNode) == (orderIds.get(node) != null) : "all fixed nodes must be ordered: " + node;
                 add(node);
@@ -365,6 +372,11 @@
     }
 
     protected void writeEdges(Node node, Edges edges, NodeOrder nodeOrder) {
+        if (node instanceof PhiNode) {
+            /* Edges are not needed for decoding, so we must not write it. */
+            return;
+        }
+
         for (int idx = 0; idx < edges.getDirectCount(); idx++) {
             if (GraphDecoder.skipDirectEdge(node, edges, idx)) {
                 /* Edge is not needed for decoding, so we must not write it. */
@@ -373,21 +385,23 @@
             Node edge = Edges.getNode(node, edges.getOffsets(), idx);
             writeOrderId(edge, nodeOrder);
         }
-        for (int idx = edges.getDirectCount(); idx < edges.getCount(); idx++) {
-            if (GraphDecoder.skipIndirectEdge(node, edges, idx, false)) {
-                /* Edge is not needed for decoding, so we must not write it. */
-                continue;
-            }
-            NodeList<Node> edgeList = Edges.getNodeList(node, edges.getOffsets(), idx);
-            if (edgeList == null) {
-                writer.putSV(-1);
-            } else {
-                writer.putSV(edgeList.size());
-                for (Node edge : edgeList) {
-                    writeOrderId(edge, nodeOrder);
+
+        if (node instanceof AbstractMergeNode && edges.type() == Edges.Type.Inputs) {
+            /* The ends of merge nodes are decoded manually when the ends are processed. */
+        } else {
+            for (int idx = edges.getDirectCount(); idx < edges.getCount(); idx++) {
+                NodeList<Node> edgeList = Edges.getNodeList(node, edges.getOffsets(), idx);
+                if (edgeList == null) {
+                    writer.putSV(-1);
+                } else {
+                    writer.putSV(edgeList.size());
+                    for (Node edge : edgeList) {
+                        writeOrderId(edge, nodeOrder);
+                    }
                 }
             }
         }
+
     }
 
     protected void writeOrderId(Node node, NodeOrder nodeOrder) {
@@ -405,16 +419,16 @@
     @SuppressWarnings("try")
     public static boolean verifyEncoding(StructuredGraph originalGraph, EncodedGraph encodedGraph, Architecture architecture) {
         StructuredGraph decodedGraph = new StructuredGraph.Builder(originalGraph.getOptions(), AllowAssumptions.YES).method(originalGraph.method()).build();
-        GraphDecoder decoder = new GraphDecoder(architecture);
-        decoder.decode(decodedGraph, encodedGraph);
+        GraphDecoder decoder = new GraphDecoder(architecture, decodedGraph);
+        decoder.decode(encodedGraph);
 
         decodedGraph.verify();
         try {
             GraphComparison.verifyGraphsEqual(originalGraph, decodedGraph);
         } catch (Throwable ex) {
             try (Debug.Scope scope = Debug.scope("GraphEncoder")) {
-                Debug.dump(Debug.INFO_LOG_LEVEL, originalGraph, "Original Graph");
-                Debug.dump(Debug.INFO_LOG_LEVEL, decodedGraph, "Decoded Graph");
+                Debug.forceDump(originalGraph, "Original Graph");
+                Debug.forceDump(decodedGraph, "Decoded Graph");
             }
             throw ex;
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -87,7 +87,7 @@
             if (stamp().equals(object().stamp())) {
                 return object();
             } else {
-                return new PiNode(object(), stamp());
+                return PiNode.create(object(), stamp());
             }
         }
         return this;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -316,7 +316,10 @@
                 FixedWithNextNode falseNext = (FixedWithNextNode) falseSucc.next();
                 NodeClass<?> nodeClass = trueNext.getNodeClass();
                 if (trueNext.getClass() == falseNext.getClass()) {
-                    if (nodeClass.equalInputs(trueNext, falseNext) && trueNext.valueEquals(falseNext)) {
+                    if (trueNext instanceof AbstractBeginNode) {
+                        // Cannot do this optimization for begin nodes, because it could
+                        // move guards above the if that need to stay below a branch.
+                    } else if (nodeClass.equalInputs(trueNext, falseNext) && trueNext.valueEquals(falseNext)) {
                         falseNext.replaceAtUsages(trueNext);
                         graph().removeFixed(falseNext);
                         GraphUtil.unlinkFixedNode(trueNext);
@@ -600,7 +603,7 @@
 
     protected void removeThroughFalseBranch(SimplifierTool tool) {
         AbstractBeginNode trueBegin = trueSuccessor();
-        graph().removeSplitPropagate(this, trueBegin, tool);
+        graph().removeSplitPropagate(this, trueBegin);
         tool.addToWorkList(trueBegin);
         if (condition() != null) {
             GraphUtil.tryKillUnused(condition());
@@ -772,9 +775,9 @@
         transferProxies(trueSuccessor(), trueMerge);
         transferProxies(falseSuccessor(), falseMerge);
 
-        cleanupMerge(tool, merge);
-        cleanupMerge(tool, trueMerge);
-        cleanupMerge(tool, falseMerge);
+        cleanupMerge(merge);
+        cleanupMerge(trueMerge);
+        cleanupMerge(falseMerge);
 
         return true;
     }
@@ -869,10 +872,10 @@
         }
     }
 
-    private void cleanupMerge(SimplifierTool tool, MergeNode merge) {
+    private void cleanupMerge(MergeNode merge) {
         if (merge != null && merge.isAlive()) {
             if (merge.forwardEndCount() == 0) {
-                GraphUtil.killCFG(merge, tool);
+                GraphUtil.killCFG(merge);
             } else if (merge.forwardEndCount() == 1) {
                 graph().reduceTrivialMerge(merge);
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiArrayNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiArrayNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -26,7 +26,6 @@
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
 
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
@@ -69,12 +68,11 @@
      * snippet.
      */
     @NodeIntrinsic(Placeholder.class)
-    public static native Object piArrayCast(Object object, int length);
+    public static native Object piArrayCastToSnippetReplaceeStamp(Object object, int length);
 
     /**
-     * A placeholder node in a snippet that will be replaced with an appropriate {@link PiArrayNode}
-     * when the snippet is instantiated. Using a placeholder means that {@link PiArrayNode} never
-     * needs to deal with {@link StampFactory#forNodeIntrinsic()} stamps.
+     * A placeholder node in a snippet that will be replaced with a {@link PiArrayNode} when the
+     * snippet is instantiated.
      */
     @NodeInfo(cycles = CYCLES_0, size = SIZE_0)
     public static class Placeholder extends PiNode.Placeholder {
@@ -88,8 +86,9 @@
         }
 
         @Override
-        public PiNode getReplacement(Stamp stampForPi) {
-            return graph().addOrUnique(new PiArrayNode(object(), length, stampForPi));
+        public void makeReplacement(Stamp snippetReplaceeStamp) {
+            PiArrayNode piArray = graph().addOrUnique(new PiArrayNode(object(), length, snippetReplaceeStamp));
+            replaceAndDelete(piArray);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,7 +25,10 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
 
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
+import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
@@ -36,6 +39,7 @@
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -70,7 +74,6 @@
 
     protected PiNode(NodeClass<? extends PiNode> c, ValueNode object, Stamp stamp, GuardingNode guard) {
         super(c, stamp, guard);
-        assert stamp != StampFactory.forNodeIntrinsic();
         this.object = object;
         this.piStamp = stamp;
         assert piStamp.isCompatible(object.stamp()) : "Object stamp not compatible to piStamp";
@@ -93,6 +96,53 @@
         this(object, StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp())));
     }
 
+    public static ValueNode create(ValueNode object, Stamp stamp) {
+        ValueNode value = canonical(object, stamp, null);
+        if (value != null) {
+            return value;
+        }
+        return new PiNode(object, stamp);
+    }
+
+    public static ValueNode create(ValueNode object, Stamp stamp, ValueNode anchor) {
+        ValueNode value = canonical(object, stamp, (GuardingNode) anchor);
+        if (value != null) {
+            return value;
+        }
+        return new PiNode(object, stamp, anchor);
+    }
+
+    public static ValueNode create(ValueNode object, ValueNode anchor) {
+        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp());
+        ValueNode value = canonical(object, stamp, (GuardingNode) anchor);
+        if (value != null) {
+            return value;
+        }
+        return new PiNode(object, stamp, anchor);
+    }
+
+    @SuppressWarnings("unused")
+    public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ValueNode anchor) {
+        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp());
+        ValueNode value = canonical(object, stamp, (GuardingNode) anchor);
+        if (value == null) {
+            value = new PiNode(object, stamp, anchor);
+        }
+        b.push(JavaKind.Object, b.recursiveAppend(value));
+        return true;
+    }
+
+    @SuppressWarnings("unused")
+    public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) {
+        Stamp stamp = StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp()));
+        ValueNode value = canonical(object, stamp, null);
+        if (value == null) {
+            value = new PiNode(object, stamp);
+        }
+        b.push(JavaKind.Object, b.recursiveAppend(value));
+        return true;
+    }
+
     public final Stamp piStamp() {
         return piStamp;
     }
@@ -124,37 +174,32 @@
         }
     }
 
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
+    public static ValueNode canonical(ValueNode object, Stamp stamp, GuardingNode guard) {
         // Use most up to date stamp.
-        Stamp computedStamp = computeStamp();
-
-        ValueNode o = object();
+        Stamp computedStamp = stamp.improveWith(object.stamp());
 
         // The pi node does not give any additional information => skip it.
-        if (computedStamp.equals(o.stamp())) {
-            return o;
+        if (computedStamp.equals(object.stamp())) {
+            return object;
         }
 
-        GuardingNode g = getGuard();
-        if (g == null) {
-
+        if (guard == null) {
             // Try to merge the pi node with a load node.
-            if (o instanceof ReadNode) {
-                ReadNode readNode = (ReadNode) o;
-                readNode.setStamp(readNode.stamp().improveWith(this.piStamp));
+            if (object instanceof ReadNode) {
+                ReadNode readNode = (ReadNode) object;
+                readNode.setStamp(readNode.stamp().improveWith(stamp));
                 return readNode;
             }
         } else {
-            for (Node n : g.asNode().usages()) {
+            for (Node n : guard.asNode().usages()) {
                 if (n instanceof PiNode) {
                     PiNode otherPi = (PiNode) n;
-                    if (o == otherPi.object() && computedStamp.equals(otherPi.stamp())) {
+                    if (object == otherPi.object() && computedStamp.equals(otherPi.stamp())) {
                         /*
                          * Two PiNodes with the same guard and same result, so return the one with
                          * the more precise piStamp.
                          */
-                        Stamp newStamp = piStamp.join(otherPi.piStamp);
+                        Stamp newStamp = stamp.join(otherPi.piStamp);
                         if (newStamp.equals(otherPi.piStamp)) {
                             return otherPi;
                         }
@@ -162,6 +207,15 @@
                 }
             }
         }
+        return null;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        Node value = canonical(object(), stamp(), getGuard());
+        if (value != null) {
+            return value;
+        }
         return this;
     }
 
@@ -226,9 +280,8 @@
     public static native Object piCast(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull);
 
     /**
-     * A placeholder node in a snippet that will be replaced with an appropriate {@link PiNode} when
-     * the snippet is instantiated. Using a placeholder means that {@link PiNode} never needs to
-     * deal with {@link StampFactory#forNodeIntrinsic()} stamps.
+     * A placeholder node in a snippet that will be replaced with a {@link PiNode} when the snippet
+     * is instantiated.
      */
     @NodeInfo(cycles = CYCLES_0, size = SIZE_0)
     public static class Placeholder extends FloatingGuardedNode {
@@ -241,7 +294,7 @@
         }
 
         protected Placeholder(NodeClass<? extends Placeholder> c, ValueNode object) {
-            super(c, StampFactory.forNodeIntrinsic(), null);
+            super(c, PlaceholderStamp.SINGLETON, null);
             this.object = object;
         }
 
@@ -250,12 +303,44 @@
         }
 
         /**
-         * Gets a new {@link PiNode} that replaces this placeholder during snippet instantiation.
+         * Replaces this node with a {@link PiNode} during snippet instantiation.
          *
          * @param snippetReplaceeStamp the stamp of the node being replace by the snippet
          */
-        public PiNode getReplacement(Stamp snippetReplaceeStamp) {
-            return graph().addOrUnique(new PiNode(object(), snippetReplaceeStamp, null));
+        public void makeReplacement(Stamp snippetReplaceeStamp) {
+            ValueNode value = graph().maybeAddOrUnique(PiNode.create(object(), snippetReplaceeStamp, null));
+            replaceAndDelete(value);
+        }
+    }
+
+    /**
+     * A stamp for {@link Placeholder} nodes which are only used in snippets. It is replaced by an
+     * actual stamp when the snippet is instantiated.
+     */
+    public static final class PlaceholderStamp extends ObjectStamp {
+        private static final PlaceholderStamp SINGLETON = new PlaceholderStamp();
+
+        public static PlaceholderStamp singleton() {
+            return SINGLETON;
+        }
+
+        private PlaceholderStamp() {
+            super(null, false, false, false);
+        }
+
+        @Override
+        public int hashCode() {
+            return System.identityHashCode(this);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return this == obj;
+        }
+
+        @Override
+        public String toString() {
+            return "PlaceholderStamp";
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/SimplifyingGraphDecoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/SimplifyingGraphDecoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.nodes;
 
+import static org.graalvm.compiler.nodeinfo.InputType.Guard;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
 
@@ -37,6 +38,9 @@
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
+import org.graalvm.compiler.nodes.java.ArrayLengthNode;
+import org.graalvm.compiler.nodes.java.LoadFieldNode;
+import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 import org.graalvm.compiler.nodes.spi.StampProvider;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.options.OptionValues;
@@ -59,6 +63,7 @@
     protected final ConstantFieldProvider constantFieldProvider;
     protected final StampProvider stampProvider;
     protected final boolean canonicalizeReads;
+    protected final CanonicalizerTool canonicalizerTool;
 
     protected class PECanonicalizerTool implements CanonicalizerTool {
 
@@ -113,7 +118,7 @@
         }
     }
 
-    @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
+    @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED, allowedUsageTypes = {Guard})
     static class CanonicalizeToNullNode extends FloatingNode implements Canonicalizable, GuardingNode {
         public static final NodeClass<CanonicalizeToNullNode> TYPE = NodeClass.create(CanonicalizeToNullNode.class);
 
@@ -127,32 +132,30 @@
         }
     }
 
-    public SimplifyingGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, StampProvider stampProvider,
-                    boolean canonicalizeReads, Architecture architecture) {
-        super(architecture);
+    public SimplifyingGraphDecoder(Architecture architecture, StructuredGraph graph, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection,
+                    ConstantFieldProvider constantFieldProvider, StampProvider stampProvider,
+                    boolean canonicalizeReads) {
+        super(architecture, graph);
         this.metaAccess = metaAccess;
         this.constantReflection = constantReflection;
         this.constantFieldProvider = constantFieldProvider;
         this.stampProvider = stampProvider;
         this.canonicalizeReads = canonicalizeReads;
+        this.canonicalizerTool = new PECanonicalizerTool(graph.getAssumptions(), graph.getOptions());
     }
 
     @Override
     protected void cleanupGraph(MethodScope methodScope) {
-        GraphUtil.normalizeLoops(methodScope.graph);
+        GraphUtil.normalizeLoops(graph);
         super.cleanupGraph(methodScope);
 
-        for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
+        for (Node node : graph.getNewNodes(methodScope.methodStartMark)) {
             if (node instanceof MergeNode) {
                 MergeNode mergeNode = (MergeNode) node;
                 if (mergeNode.forwardEndCount() == 1) {
-                    methodScope.graph.reduceTrivialMerge(mergeNode);
+                    graph.reduceTrivialMerge(mergeNode);
                 }
-            }
-        }
-
-        for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
-            if (node instanceof BeginNode || node instanceof KillingBeginNode) {
+            } else if (node instanceof BeginNode || node instanceof KillingBeginNode) {
                 if (!(node.predecessor() instanceof ControlSplitNode) && node.hasNoUsages()) {
                     GraphUtil.unlinkFixedNode((AbstractBeginNode) node);
                     node.safeDelete();
@@ -160,7 +163,7 @@
             }
         }
 
-        for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
+        for (Node node : graph.getNewNodes(methodScope.methodStartMark)) {
             GraphUtil.tryKillUnused(node);
         }
     }
@@ -187,7 +190,32 @@
 
     @Override
     protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
-        if (node instanceof IfNode) {
+        Node canonical = canonicalizeFixedNode(node);
+        if (canonical != node) {
+            handleCanonicalization(loopScope, nodeOrderId, node, canonical);
+        }
+    }
+
+    private Node canonicalizeFixedNode(FixedNode node) {
+        if (node instanceof LoadFieldNode) {
+            LoadFieldNode loadFieldNode = (LoadFieldNode) node;
+            return loadFieldNode.canonical(canonicalizerTool);
+        } else if (node instanceof FixedGuardNode) {
+            FixedGuardNode guard = (FixedGuardNode) node;
+            if (guard.getCondition() instanceof LogicConstantNode) {
+                LogicConstantNode condition = (LogicConstantNode) guard.getCondition();
+                if (condition.getValue() == guard.isNegated()) {
+                    DeoptimizeNode deopt = new DeoptimizeNode(guard.getAction(), guard.getReason(), guard.getSpeculation());
+                    if (guard.stateBefore() != null) {
+                        deopt.setStateBefore(guard.stateBefore());
+                    }
+                    return deopt;
+                } else {
+                    return null;
+                }
+            }
+            return node;
+        } else if (node instanceof IfNode) {
             IfNode ifNode = (IfNode) node;
             if (ifNode.condition() instanceof LogicNegationNode) {
                 ifNode.eliminateNegation();
@@ -197,73 +225,55 @@
                 AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
                 AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
 
-                methodScope.graph.removeSplit(ifNode, survivingSuccessor);
+                graph.removeSplit(ifNode, survivingSuccessor);
                 assert deadSuccessor.next() == null : "must not be parsed yet";
                 deadSuccessor.safeDelete();
             }
-
+            return node;
+        } else if (node instanceof LoadIndexedNode) {
+            LoadIndexedNode loadIndexedNode = (LoadIndexedNode) node;
+            return loadIndexedNode.canonical(canonicalizerTool);
+        } else if (node instanceof ArrayLengthNode) {
+            ArrayLengthNode arrayLengthNode = (ArrayLengthNode) node;
+            return arrayLengthNode.canonical(canonicalizerTool);
         } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) {
             IntegerSwitchNode switchNode = (IntegerSwitchNode) node;
             int value = switchNode.value().asJavaConstant().asInt();
             AbstractBeginNode survivingSuccessor = switchNode.successorAtKey(value);
             List<Node> allSuccessors = switchNode.successors().snapshot();
 
-            methodScope.graph.removeSplit(switchNode, survivingSuccessor);
+            graph.removeSplit(switchNode, survivingSuccessor);
             for (Node successor : allSuccessors) {
                 if (successor != survivingSuccessor) {
                     assert ((AbstractBeginNode) successor).next() == null : "must not be parsed yet";
                     successor.safeDelete();
                 }
             }
-
-        } else if (node instanceof FixedGuardNode) {
-            FixedGuardNode guard = (FixedGuardNode) node;
-            if (guard.getCondition() instanceof LogicConstantNode) {
-                LogicConstantNode condition = (LogicConstantNode) guard.getCondition();
-                Node canonical;
-                if (condition.getValue() == guard.isNegated()) {
-                    DeoptimizeNode deopt = new DeoptimizeNode(guard.getAction(), guard.getReason(), guard.getSpeculation());
-                    if (guard.stateBefore() != null) {
-                        deopt.setStateBefore(guard.stateBefore());
-                    }
-                    canonical = deopt;
-                } else {
-                    /*
-                     * The guard is unnecessary, but we cannot remove the node completely yet
-                     * because there might be nodes that use it as a guard input. Therefore, we
-                     * replace it with a more lightweight node (which is floating and has no
-                     * inputs).
-                     */
-                    canonical = new CanonicalizeToNullNode(node.stamp);
-                }
-                handleCanonicalization(methodScope, loopScope, nodeOrderId, node, canonical);
-            }
-
+            return node;
         } else if (node instanceof Canonicalizable) {
-            Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool(methodScope.graph.getAssumptions(), methodScope.graph.getOptions()));
-            if (canonical != node) {
-                handleCanonicalization(methodScope, loopScope, nodeOrderId, node, canonical);
-            }
+            return ((Canonicalizable) node).canonical(canonicalizerTool);
+        } else {
+            return node;
         }
     }
 
-    private void handleCanonicalization(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node, Node c) {
-        Node canonical = c;
+    private static Node canonicalizeFixedNodeToNull(FixedNode node) {
+        /*
+         * When a node is unnecessary, we must not remove it right away because there might be nodes
+         * that use it as a guard input. Therefore, we replace it with a more lightweight node
+         * (which is floating and has no inputs).
+         */
+        return new CanonicalizeToNullNode(node.stamp);
+    }
 
-        if (canonical == null) {
-            /*
-             * This is a possible return value of canonicalization. However, we might need to add
-             * additional usages later on for which we need a node. Therefore, we just do nothing
-             * and leave the node in place.
-             */
-            return;
-        }
-
+    private void handleCanonicalization(LoopScope loopScope, int nodeOrderId, FixedNode node, Node c) {
+        assert c != node : "unnecessary call";
+        Node canonical = c == null ? canonicalizeFixedNodeToNull(node) : c;
         if (!canonical.isAlive()) {
             assert !canonical.isDeleted();
-            canonical = methodScope.graph.addOrUniqueWithInputs(canonical);
+            canonical = graph.addOrUniqueWithInputs(canonical);
             if (canonical instanceof FixedWithNextNode) {
-                methodScope.graph.addBeforeFixed(node, (FixedWithNextNode) canonical);
+                graph.addBeforeFixed(node, (FixedWithNextNode) canonical);
             } else if (canonical instanceof ControlSinkNode) {
                 FixedWithNextNode predecessor = (FixedWithNextNode) node.predecessor();
                 predecessor.setNext((ControlSinkNode) canonical);
@@ -291,7 +301,7 @@
             ((ValueNode) node).inferStamp();
         }
         if (node instanceof Canonicalizable) {
-            Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool(methodScope.graph.getAssumptions(), methodScope.graph.getOptions()));
+            Node canonical = ((Canonicalizable) node).canonical(canonicalizerTool);
             if (canonical == null) {
                 /*
                  * This is a possible return value of canonicalization. However, we might need to
@@ -301,10 +311,9 @@
             } else if (canonical != node) {
                 if (!canonical.isAlive()) {
                     assert !canonical.isDeleted();
-                    canonical = methodScope.graph.addOrUniqueWithInputs(canonical);
+                    canonical = graph.addOrUniqueWithInputs(canonical);
                 }
                 assert node.hasNoUsages();
-                // methodScope.graph.replaceFloating((FloatingNode) node, canonical);
                 return canonical;
             }
         }
@@ -317,6 +326,6 @@
          * In contrast to the base class implementation, we do not need to exactly reproduce the
          * encoded graph. Since we do canonicalization, we also want nodes to be unique.
          */
-        return methodScope.graph.addOrUnique(node);
+        return graph.addOrUnique(node);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Thu Apr 06 14:31:32 2017 -0700
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
-import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
@@ -553,12 +552,8 @@
         node.safeDelete();
     }
 
+    @SuppressWarnings("static-method")
     public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) {
-        removeSplitPropagate(node, survivingSuccessor, null);
-    }
-
-    @SuppressWarnings("static-method")
-    public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor, SimplifierTool tool) {
         assert node != null;
         assert node.hasNoUsages();
         assert survivingSuccessor != null;
@@ -569,7 +564,7 @@
         for (Node successor : snapshot) {
             if (successor != null && successor.isAlive()) {
                 if (successor != survivingSuccessor) {
-                    GraphUtil.killCFG((FixedNode) successor, tool);
+                    GraphUtil.killCFG((FixedNode) successor);
                 }
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -57,28 +57,16 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
+        }
+        if (x.isConstant() && !y.isConstant()) {
+            return canonical(null, op, y, x);
         } else {
-            return new AddNode(x, y).maybeCommuteInputs();
+            return canonical(null, op, x, y);
         }
     }
 
-    @Override
-    public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode ret = super.canonical(tool, forX, forY);
-        if (ret != this) {
-            return ret;
-        }
-
-        if (forX.isConstant() && !forY.isConstant()) {
-            // we try to swap and canonicalize
-            ValueNode improvement = canonical(tool, forY, forX);
-            if (improvement != this) {
-                return improvement;
-            }
-            // if this fails we only swap
-            return new AddNode(forY, forX);
-        }
-        BinaryOp<Add> op = getOp(forX, forY);
+    private static ValueNode canonical(AddNode addNode, BinaryOp<Add> op, ValueNode forX, ValueNode forY) {
+        AddNode self = addNode;
         boolean associative = op.isAssociative();
         if (associative) {
             if (forX instanceof SubNode) {
@@ -101,10 +89,10 @@
             if (op.isNeutral(c)) {
                 return forX;
             }
-            if (associative) {
+            if (associative && self != null) {
                 // canonicalize expressions like "(a + 1) + 2"
-                ValueNode reassociated = reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
-                if (reassociated != this) {
+                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
+                if (reassociated != self) {
                     return reassociated;
                 }
             }
@@ -114,7 +102,30 @@
         } else if (forY instanceof NegateNode) {
             return BinaryArithmeticNode.sub(forX, ((NegateNode) forY).getValue());
         }
-        return this;
+        if (self == null) {
+            self = (AddNode) new AddNode(forX, forY).maybeCommuteInputs();
+        }
+        return self;
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        ValueNode ret = super.canonical(tool, forX, forY);
+        if (ret != this) {
+            return ret;
+        }
+
+        if (forX.isConstant() && !forY.isConstant()) {
+            // we try to swap and canonicalize
+            ValueNode improvement = canonical(tool, forY, forX);
+            if (improvement != this) {
+                return improvement;
+            }
+            // if this fails we only swap
+            return new AddNode(forY, forX);
+        }
+        BinaryOp<Add> op = getOp(forX, forY);
+        return canonical(this, op, forX, forY);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -57,9 +57,8 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new AndNode(x, y).maybeCommuteInputs();
         }
+        return canonical(null, op, stamp, x, y);
     }
 
     @Override
@@ -69,6 +68,10 @@
             return ret;
         }
 
+        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
@@ -77,15 +80,15 @@
         }
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
-            if (getOp(forX, forY).isNeutral(c)) {
+            if (op.isNeutral(c)) {
                 return forX;
             }
 
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long rawY = ((PrimitiveConstant) c).asLong();
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp()));
+                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp));
                 if ((rawY & mask) == 0) {
-                    return ConstantNode.forIntegerStamp(stamp(), 0);
+                    return ConstantNode.forIntegerStamp(stamp, 0);
                 }
                 if (forX instanceof SignExtendNode) {
                     SignExtendNode ext = (SignExtendNode) forX;
@@ -100,9 +103,12 @@
                 }
             }
 
-            return reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
         }
-        return this;
+        if (forX instanceof NotNode && forY instanceof NotNode) {
+            return new NotNode(OrNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue()));
+        }
+        return self != null ? self : new AndNode(forX, forY).maybeCommuteInputs();
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -44,6 +44,7 @@
 
 import jdk.vm.ci.meta.Constant;
 import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.PrimitiveConstant;
 
 @NodeInfo(cycles = CYCLES_1)
 public abstract class CompareNode extends BinaryOpLogicNode implements Canonicalizable.Binary<ValueNode> {
@@ -151,12 +152,19 @@
     }
 
     public static LogicNode tryConstantFold(Condition condition, ValueNode forX, ValueNode forY, ConstantReflectionProvider constantReflection, boolean unorderedIsTrue) {
-        if (forX.isConstant() && forY.isConstant() && constantReflection != null) {
+        if (forX.isConstant() && forY.isConstant() && (constantReflection != null || forX.asConstant() instanceof PrimitiveConstant)) {
             return LogicConstantNode.forBoolean(condition.foldCondition(forX.asConstant(), forY.asConstant(), constantReflection, unorderedIsTrue));
         }
         return null;
     }
 
+    public static LogicNode tryConstantFoldPrimitive(Condition condition, ValueNode forX, ValueNode forY, boolean unorderedIsTrue) {
+        if (forX.asConstant() instanceof PrimitiveConstant && forY.asConstant() instanceof PrimitiveConstant) {
+            return LogicConstantNode.forBoolean(condition.foldCondition((PrimitiveConstant) forX.asConstant(), (PrimitiveConstant) forY.asConstant(), unorderedIsTrue));
+        }
+        return null;
+    }
+
     /**
      * Does this operation represent an identity check such that for x == y, x is exactly the same
      * thing as y. This is generally true except for some floating point comparisons.
@@ -221,7 +229,7 @@
 
     public static LogicNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
         LogicNode result = createCompareNode(condition, x, y, constantReflection);
-        return (result.graph() == null ? graph.unique(result) : result);
+        return (result.graph() == null ? graph.addOrUniqueWithInputs(result) : result);
     }
 
     public static LogicNode createCompareNode(Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
@@ -237,15 +245,15 @@
                 comparison = PointerEqualsNode.create(x, y);
             } else {
                 assert x.getStackKind().isNumericInteger();
-                comparison = IntegerEqualsNode.create(x, y, constantReflection);
+                comparison = IntegerEqualsNode.create(x, y);
             }
         } else if (condition == Condition.LT) {
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerLessThanNode.create(x, y, constantReflection);
+            comparison = IntegerLessThanNode.create(x, y);
         } else {
             assert condition == Condition.BT;
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerBelowNode.create(x, y, constantReflection);
+            comparison = IntegerBelowNode.create(x, y);
         }
 
         return comparison;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -83,6 +83,10 @@
         if (synonym != null) {
             return synonym;
         }
+        ValueNode result = canonicalizeConditional(condition, trueValue, falseValue, trueValue.stamp().meet(falseValue.stamp()));
+        if (result != null) {
+            return result;
+        }
         return new ConditionalNode(condition, trueValue, falseValue);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -58,9 +58,8 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new DivNode(x, y);
         }
+        return canonical(null, op, x, y);
     }
 
     @Override
@@ -70,9 +69,13 @@
             return ret;
         }
 
+        return canonical(this, getOp(forX, forY), forX, forY);
+    }
+
+    private static ValueNode canonical(DivNode self, BinaryOp<Div> op, ValueNode forX, ValueNode forY) {
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
-            if (getOp(forX, forY).isNeutral(c)) {
+            if (op.isNeutral(c)) {
                 return forX;
             }
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
@@ -88,14 +91,14 @@
                 }
                 if (divResult != null) {
                     if (signFlip) {
-                        return new NegateNode(divResult);
+                        return NegateNode.create(divResult);
                     } else {
                         return divResult;
                     }
                 }
             }
         }
-        return this;
+        return self != null ? self : new DivNode(forX, forY);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.TriState;
 
 @NodeInfo(shortName = "==", cycles = NodeCycles.CYCLES_3)
@@ -50,8 +49,8 @@
         assert x.stamp().isCompatible(y.stamp());
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-        LogicNode result = CompareNode.tryConstantFold(Condition.EQ, x, y, constantReflection, false);
+    public static LogicNode create(ValueNode x, ValueNode y) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false);
         if (result != null) {
             return result;
         } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -36,7 +36,6 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.TriState;
 
 @NodeInfo(shortName = "<", cycles = NodeCycles.CYCLES_3)
@@ -49,8 +48,8 @@
         assert x.stamp().isCompatible(y.stamp());
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue, ConstantReflectionProvider constantReflection) {
-        LogicNode result = CompareNode.tryConstantFold(Condition.LT, x, y, constantReflection, unorderedIsTrue);
+    public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.LT, x, y, unorderedIsTrue);
         if (result != null) {
             return result;
         } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,7 +32,6 @@
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.code.CodeUtil;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 
 @NodeInfo(shortName = "|<|")
 public final class IntegerBelowNode extends IntegerLowerThanNode {
@@ -45,8 +44,8 @@
         assert y.stamp() instanceof IntegerStamp;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-        return OP.create(x, y, constantReflection);
+    public static LogicNode create(ValueNode x, ValueNode y) {
+        return OP.create(x, y);
     }
 
     @Override
@@ -108,7 +107,7 @@
         }
 
         @Override
-        protected IntegerLowerThanNode create(ValueNode x, ValueNode y) {
+        protected IntegerLowerThanNode createNode(ValueNode x, ValueNode y) {
             return new IntegerBelowNode(x, y);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -40,7 +40,6 @@
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
 import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.PrimitiveConstant;
 import jdk.vm.ci.meta.TriState;
@@ -55,8 +54,8 @@
         assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-        LogicNode result = CompareNode.tryConstantFold(Condition.EQ, x, y, constantReflection, false);
+    public static LogicNode create(ValueNode x, ValueNode y) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false);
         if (result != null) {
             return result;
         } else {
@@ -117,6 +116,29 @@
         } else if (forX.stamp().alwaysDistinct(forY.stamp())) {
             return LogicConstantNode.contradiction();
         }
+        if (forX instanceof AddNode && forY instanceof AddNode) {
+            AddNode addX = (AddNode) forX;
+            AddNode addY = (AddNode) forY;
+            ValueNode v1 = null;
+            ValueNode v2 = null;
+            if (addX.getX() == addY.getX()) {
+                v1 = addX.getY();
+                v2 = addY.getY();
+            } else if (addX.getX() == addY.getY()) {
+                v1 = addX.getY();
+                v2 = addY.getX();
+            } else if (addX.getY() == addY.getX()) {
+                v1 = addX.getX();
+                v2 = addY.getY();
+            } else if (addX.getY() == addY.getY()) {
+                v1 = addX.getX();
+                v2 = addY.getX();
+            }
+            if (v1 != null) {
+                assert v2 != null;
+                return create(v1, v2);
+            }
+        }
         return super.canonical(tool, forX, forY);
     }
 
@@ -130,14 +152,14 @@
                 // nonConstant can only be 0 or 1 (respective -1), test against 0 instead of 1
                 // (respective -1) for a more canonical graph and also to allow for faster execution
                 // on specific platforms.
-                return LogicNegationNode.create(IntegerEqualsNode.create(nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0), null));
+                return LogicNegationNode.create(IntegerEqualsNode.create(nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0)));
             } else if (primitiveConstant.asLong() == 0) {
                 if (nonConstant instanceof AndNode) {
                     AndNode andNode = (AndNode) nonConstant;
                     return new IntegerTestNode(andNode.getX(), andNode.getY());
                 } else if (nonConstant instanceof SubNode) {
                     SubNode subNode = (SubNode) nonConstant;
-                    return IntegerEqualsNode.create(subNode.getX(), subNode.getY(), tool.getConstantReflection());
+                    return IntegerEqualsNode.create(subNode.getX(), subNode.getY());
                 } else if (nonConstant instanceof ShiftNode && nonConstant.stamp() instanceof IntegerStamp) {
                     if (nonConstant instanceof LeftShiftNode) {
                         LeftShiftNode shift = (LeftShiftNode) nonConstant;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -31,15 +31,14 @@
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.NodeClass;
-import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.code.CodeUtil;
 import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.PrimitiveConstant;
@@ -55,8 +54,8 @@
         assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-        return OP.create(x, y, constantReflection);
+    public static LogicNode create(ValueNode x, ValueNode y) {
+        return OP.create(x, y);
     }
 
     @Override
@@ -91,79 +90,6 @@
     }
 
     @Override
-    public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode result = super.canonical(tool, forX, forY);
-        if (result != this) {
-            return result;
-        }
-        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
-            if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
-                return new IntegerBelowNode(forX, forY);
-            }
-        }
-        if (forY.isConstant() && forY.asConstant().isDefaultForKind() && forX instanceof SubNode) {
-            // (x - y) < 0 when x - y is known not to underflow <=> x < y
-            SubNode sub = (SubNode) forX;
-            IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp();
-            IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp();
-            long minValue = CodeUtil.minValue(xStamp.getBits());
-            long maxValue = CodeUtil.maxValue(xStamp.getBits());
-
-            if (!subtractMayUnderflow(xStamp.lowerBound(), yStamp.upperBound(), minValue) && !subtractMayOverflow(xStamp.upperBound(), yStamp.lowerBound(), maxValue)) {
-                return new IntegerLessThanNode(sub.getX(), sub.getY());
-            }
-        }
-
-        int bits = ((IntegerStamp) getX().stamp()).getBits();
-        assert ((IntegerStamp) getY().stamp()).getBits() == bits;
-        long min = OP.minValue(bits);
-        long xResidue = 0;
-        ValueNode left = null;
-        JavaConstant leftCst = null;
-        if (forX instanceof AddNode) {
-            AddNode xAdd = (AddNode) forX;
-            if (xAdd.getY().isJavaConstant()) {
-                long xCst = xAdd.getY().asJavaConstant().asLong();
-                xResidue = xCst - min;
-                left = xAdd.getX();
-            }
-        } else if (forX.isJavaConstant()) {
-            leftCst = forX.asJavaConstant();
-        }
-        if (left != null || leftCst != null) {
-            long yResidue = 0;
-            ValueNode right = null;
-            JavaConstant rightCst = null;
-            if (forY instanceof AddNode) {
-                AddNode yAdd = (AddNode) forY;
-                if (yAdd.getY().isJavaConstant()) {
-                    long yCst = yAdd.getY().asJavaConstant().asLong();
-                    yResidue = yCst - min;
-                    right = yAdd.getX();
-                }
-            } else if (forY.isJavaConstant()) {
-                rightCst = forY.asJavaConstant();
-            }
-            if (right != null || rightCst != null) {
-                if ((xResidue == 0 && left != null) || (yResidue == 0 && right != null)) {
-                    if (left == null) {
-                        left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min);
-                    } else if (xResidue != 0) {
-                        left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue));
-                    }
-                    if (right == null) {
-                        right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min);
-                    } else if (yResidue != 0) {
-                        right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue));
-                    }
-                    return new IntegerBelowNode(left, right);
-                }
-            }
-        }
-        return this;
-    }
-
-    @Override
     protected CompareNode duplicateModified(ValueNode newX, ValueNode newY) {
         if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) {
             return new FloatLessThanNode(newX, newY, true);
@@ -176,12 +102,104 @@
     public static class LessThanOp extends LowerOp {
 
         @Override
+        protected LogicNode findSynonym(ValueNode forX, ValueNode forY) {
+            LogicNode result = super.findSynonym(forX, forY);
+            if (result != null) {
+                return result;
+            }
+            if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
+                if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
+                    return new IntegerBelowNode(forX, forY);
+                }
+            }
+            if (forY.isConstant() && forX instanceof SubNode) {
+                SubNode sub = (SubNode) forX;
+                ValueNode xx = null;
+                ValueNode yy = null;
+                boolean negate = false;
+                if (forY.asConstant().isDefaultForKind()) {
+                    // (x - y) < 0 when x - y is known not to underflow <=> x < y
+                    xx = sub.getX();
+                    yy = sub.getY();
+                } else if (forY.isJavaConstant() && forY.asJavaConstant().asLong() == 1) {
+                    // (x - y) < 1 when x - y is known not to underflow <=> !(y < x)
+                    xx = sub.getY();
+                    yy = sub.getX();
+                    negate = true;
+                }
+                if (xx != null) {
+                    assert yy != null;
+                    IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp();
+                    IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp();
+                    long minValue = CodeUtil.minValue(xStamp.getBits());
+                    long maxValue = CodeUtil.maxValue(xStamp.getBits());
+
+                    if (!subtractMayUnderflow(xStamp.lowerBound(), yStamp.upperBound(), minValue) && !subtractMayOverflow(xStamp.upperBound(), yStamp.lowerBound(), maxValue)) {
+                        LogicNode logic = new IntegerLessThanNode(xx, yy);
+                        if (negate) {
+                            logic = LogicNegationNode.create(logic);
+                        }
+                        return logic;
+                    }
+                }
+            }
+
+            int bits = ((IntegerStamp) forX.stamp()).getBits();
+            assert ((IntegerStamp) forY.stamp()).getBits() == bits;
+            long min = OP.minValue(bits);
+            long xResidue = 0;
+            ValueNode left = null;
+            JavaConstant leftCst = null;
+            if (forX instanceof AddNode) {
+                AddNode xAdd = (AddNode) forX;
+                if (xAdd.getY().isJavaConstant()) {
+                    long xCst = xAdd.getY().asJavaConstant().asLong();
+                    xResidue = xCst - min;
+                    left = xAdd.getX();
+                }
+            } else if (forX.isJavaConstant()) {
+                leftCst = forX.asJavaConstant();
+            }
+            if (left != null || leftCst != null) {
+                long yResidue = 0;
+                ValueNode right = null;
+                JavaConstant rightCst = null;
+                if (forY instanceof AddNode) {
+                    AddNode yAdd = (AddNode) forY;
+                    if (yAdd.getY().isJavaConstant()) {
+                        long yCst = yAdd.getY().asJavaConstant().asLong();
+                        yResidue = yCst - min;
+                        right = yAdd.getX();
+                    }
+                } else if (forY.isJavaConstant()) {
+                    rightCst = forY.asJavaConstant();
+                }
+                if (right != null || rightCst != null) {
+                    if ((xResidue == 0 && left != null) || (yResidue == 0 && right != null)) {
+                        if (left == null) {
+                            left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min);
+                        } else if (xResidue != 0) {
+                            left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue));
+                        }
+                        if (right == null) {
+                            right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min);
+                        } else if (yResidue != 0) {
+                            right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue));
+                        }
+                        return new IntegerBelowNode(left, right);
+                    }
+                }
+            }
+            return null;
+        }
+
+        @Override
         protected Condition getCondition() {
             return LT;
         }
 
         @Override
-        protected IntegerLowerThanNode create(ValueNode x, ValueNode y) {
+        protected IntegerLowerThanNode createNode(ValueNode x, ValueNode y) {
             return new IntegerLessThanNode(x, y);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -28,13 +28,13 @@
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.TriState;
 
 /**
@@ -65,51 +65,9 @@
         if (synonym != null) {
             return synonym;
         }
-        if (forY.stamp() instanceof IntegerStamp) {
-            IntegerStamp yStamp = (IntegerStamp) forY.stamp();
-            if (forX.isConstant() && forX.asJavaConstant().asLong() == getOp().minValue(yStamp.getBits())) {
-                // MIN < y is the same as y != MIN
-                return LogicNegationNode.create(CompareNode.createCompareNode(Condition.EQ, forY, forX, tool.getConstantReflection()));
-            }
-            if (forY instanceof AddNode) {
-                AddNode addNode = (AddNode) forY;
-                ValueNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true);
-                if (canonical != null) {
-                    return canonical;
-                }
-            }
-            if (forX instanceof AddNode) {
-                AddNode addNode = (AddNode) forX;
-                ValueNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false);
-                if (canonical != null) {
-                    return canonical;
-                }
-            }
-        }
         return this;
     }
 
-    private ValueNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean negated, boolean strict) {
-        // x < x + a
-        Stamp succeedingXStamp;
-        if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) {
-            succeedingXStamp = getOp().getSucceedingStampForXLowerXPlusA(negated, strict, (IntegerStamp) addNode.getY().stamp());
-        } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) {
-            succeedingXStamp = getOp().getSucceedingStampForXLowerXPlusA(negated, strict, (IntegerStamp) addNode.getX().stamp());
-        } else {
-            return null;
-        }
-        succeedingXStamp = forX.stamp().join(succeedingXStamp);
-        if (succeedingXStamp.isEmpty()) {
-            return LogicConstantNode.contradiction();
-        }
-        /*
-         * since getSucceedingStampForXLowerXPlusA is only best effort,
-         * succeedingXStamp.equals(xStamp) does not imply tautology
-         */
-        return null;
-    }
-
     @Override
     public Stamp getSucceedingStampForX(boolean negated, Stamp xStampGeneric, Stamp yStampGeneric) {
         return getSucceedingStampForX(negated, !negated, xStampGeneric, yStampGeneric, getX(), getY());
@@ -196,10 +154,10 @@
 
         protected abstract Condition getCondition();
 
-        protected abstract IntegerLowerThanNode create(ValueNode x, ValueNode y);
+        protected abstract IntegerLowerThanNode createNode(ValueNode x, ValueNode y);
 
-        public LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-            LogicNode result = CompareNode.tryConstantFold(getCondition(), x, y, constantReflection, false);
+        public LogicNode create(ValueNode x, ValueNode y) {
+            LogicNode result = CompareNode.tryConstantFoldPrimitive(getCondition(), x, y, false);
             if (result != null) {
                 return result;
             } else {
@@ -207,7 +165,7 @@
                 if (result != null) {
                     return result;
                 }
-                return create(x, y);
+                return createNode(x, y);
             }
         }
 
@@ -221,6 +179,73 @@
             } else if (fold.isFalse()) {
                 return LogicConstantNode.contradiction();
             }
+            if (forY.stamp() instanceof IntegerStamp) {
+                IntegerStamp yStamp = (IntegerStamp) forY.stamp();
+                int bits = yStamp.getBits();
+                if (forX.isJavaConstant() && !forY.isConstant()) {
+                    // bring the constant on the right
+                    long xValue = forX.asJavaConstant().asLong();
+                    if (xValue != maxValue(bits)) {
+                        // c < x <=> !(c >= x) <=> !(x <= c) <=> !(x < c + 1)
+                        return LogicNegationNode.create(create(forY, ConstantNode.forIntegerStamp(yStamp, xValue + 1)));
+                    }
+                }
+                if (forY.isJavaConstant()) {
+                    long yValue = forY.asJavaConstant().asLong();
+                    if (yValue == maxValue(bits)) {
+                        // x < MAX <=> x != MAX
+                        return LogicNegationNode.create(IntegerEqualsNode.create(forX, forY));
+                    }
+                    if (yValue == minValue(bits) + 1) {
+                        // x < MIN + 1 <=> x <= MIN <=> x == MIN
+                        return IntegerEqualsNode.create(forX, ConstantNode.forIntegerStamp(yStamp, minValue(bits)));
+                    }
+                } else if (forY instanceof AddNode) {
+                    AddNode addNode = (AddNode) forY;
+                    LogicNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true);
+                    if (canonical != null) {
+                        return canonical;
+                    }
+                }
+                if (forX instanceof AddNode) {
+                    AddNode addNode = (AddNode) forX;
+                    LogicNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false);
+                    if (canonical != null) {
+                        return canonical;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private LogicNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean mirrored, boolean strict) {
+            // x < x + a
+            IntegerStamp succeedingXStamp;
+            boolean exact;
+            if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) {
+                IntegerStamp aStamp = (IntegerStamp) addNode.getY().stamp();
+                succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp);
+                exact = aStamp.lowerBound() == aStamp.upperBound();
+            } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) {
+                IntegerStamp aStamp = (IntegerStamp) addNode.getX().stamp();
+                succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp);
+                exact = aStamp.lowerBound() == aStamp.upperBound();
+            } else {
+                return null;
+            }
+            if (succeedingXStamp.join(forX.stamp()).isEmpty()) {
+                return LogicConstantNode.contradiction();
+            } else if (exact && !succeedingXStamp.isEmpty()) {
+                int bits = succeedingXStamp.getBits();
+                if (compare(lowerBound(succeedingXStamp), minValue(bits)) > 0) {
+                    assert upperBound(succeedingXStamp) == maxValue(bits);
+                    // x must be in [L..MAX] <=> x >= L <=> !(x < L)
+                    return LogicNegationNode.create(create(forX, ConstantNode.forIntegerStamp(succeedingXStamp, lowerBound(succeedingXStamp))));
+                } else if (compare(upperBound(succeedingXStamp), maxValue(bits)) < 0) {
+                    // x must be in [MIN..H] <=> x <= H <=> !(H < x)
+                    return LogicNegationNode.create(create(ConstantNode.forIntegerStamp(succeedingXStamp, upperBound(succeedingXStamp)), forX));
+                }
+            }
             return null;
         }
 
@@ -268,7 +293,7 @@
             return null;
         }
 
-        protected IntegerStamp getSucceedingStampForXLowerXPlusA(boolean negated, boolean strict, IntegerStamp a) {
+        protected IntegerStamp getSucceedingStampForXLowerXPlusA(boolean mirrored, boolean strict, IntegerStamp a) {
             int bits = a.getBits();
             long min = minValue(bits);
             long max = maxValue(bits);
@@ -286,7 +311,7 @@
              * This does not use upper/lowerBound from LowerOp because it's about the (signed)
              * addition not the comparison.
              */
-            if (negated) {
+            if (mirrored) {
                 if (a.contains(0)) {
                     // a may be zero
                     return a.unrestricted();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -57,8 +57,7 @@
     }
 
     public static LogicNode create(ValueNode forValue) {
-        LogicNode result = tryCanonicalize(forValue);
-        return result == null ? new IsNullNode(GraphUtil.skipPi(forValue)) : result;
+        return canonicalized(null, forValue);
     }
 
     public static LogicNode tryCanonicalize(ValueNode forValue) {
@@ -84,7 +83,11 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        return canonicalized(this, forValue);
+    }
 
+    private static LogicNode canonicalized(IsNullNode isNullNode, ValueNode forValue) {
+        IsNullNode self = isNullNode;
         LogicNode result = tryCanonicalize(forValue);
         if (result != null) {
             return result;
@@ -101,7 +104,10 @@
             }
         }
 
-        return this;
+        if (self == null) {
+            self = new IsNullNode(GraphUtil.skipPi(forValue));
+        }
+        return self;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -24,6 +24,8 @@
 
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shl;
+import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
@@ -43,6 +45,17 @@
         super(TYPE, ArithmeticOpTable::getShl, x, y);
     }
 
+    public static ValueNode create(ValueNode x, ValueNode y) {
+        ArithmeticOpTable.ShiftOp<Shl> op = ArithmeticOpTable.forStamp(x.stamp()).getShl();
+        Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp());
+        ValueNode value = ShiftNode.canonical(op, stamp, x, y);
+        if (value != null) {
+            return value;
+        }
+
+        return canonical(null, op, stamp, x, y);
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode ret = super.canonical(tool, forX, forY);
@@ -50,10 +63,15 @@
             return ret;
         }
 
+        return canonical(this, getArithmeticOp(), stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(LeftShiftNode leftShiftNode, ArithmeticOpTable.ShiftOp<Shl> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+        LeftShiftNode self = leftShiftNode;
         if (forY.isConstant()) {
             int amount = forY.asJavaConstant().asInt();
-            int originalAmout = amount;
-            int mask = getShiftAmountMask();
+            int originalAmount = amount;
+            int mask = op.getShiftAmountMask(stamp);
             amount &= mask;
             if (amount == 0) {
                 return forX;
@@ -65,24 +83,27 @@
                     if (other instanceof LeftShiftNode) {
                         int total = amount + otherAmount;
                         if (total != (total & mask)) {
-                            return ConstantNode.forIntegerKind(getStackKind(), 0);
+                            return ConstantNode.forIntegerKind(stamp.getStackKind(), 0);
                         }
                         return new LeftShiftNode(other.getX(), ConstantNode.forInt(total));
                     } else if ((other instanceof RightShiftNode || other instanceof UnsignedRightShiftNode) && otherAmount == amount) {
-                        if (getStackKind() == JavaKind.Long) {
+                        if (stamp.getStackKind() == JavaKind.Long) {
                             return new AndNode(other.getX(), ConstantNode.forLong(-1L << amount));
                         } else {
-                            assert getStackKind() == JavaKind.Int;
+                            assert stamp.getStackKind() == JavaKind.Int;
                             return new AndNode(other.getX(), ConstantNode.forInt(-1 << amount));
                         }
                     }
                 }
             }
-            if (originalAmout != amount) {
+            if (originalAmount != amount) {
                 return new LeftShiftNode(forX, ConstantNode.forInt(amount));
             }
         }
-        return this;
+        if (self == null) {
+            self = new LeftShiftNode(forX, forY);
+        }
+        return self;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -60,9 +60,8 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new MulNode(x, y).maybeCommuteInputs();
         }
+        return canonical(null, op, stamp, x, y);
     }
 
     @Override
@@ -81,8 +80,12 @@
             // if this fails we only swap
             return new MulNode(forY, forX);
         }
+        BinaryOp<Mul> op = getOp(forX, forY);
+        return canonical(this, op, stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(MulNode self, BinaryOp<Mul> op, Stamp stamp, ValueNode forX, ValueNode forY) {
         if (forY.isConstant()) {
-            BinaryOp<Mul> op = getOp(forX, forY);
             Constant c = forY.asConstant();
             if (op.isNeutral(c)) {
                 return forX;
@@ -96,7 +99,7 @@
                 } else if (i == 1) {
                     return forX;
                 } else if (i == -1) {
-                    return new NegateNode(forX);
+                    return NegateNode.create(forX);
                 } else if (i > 0) {
                     if (CodeUtil.isPowerOf2(i)) {
                         return new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i)));
@@ -107,17 +110,17 @@
                     }
                 } else if (i < 0) {
                     if (CodeUtil.isPowerOf2(-i)) {
-                        return new NegateNode(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(-i))));
+                        return NegateNode.create(LeftShiftNode.create(forX, ConstantNode.forInt(CodeUtil.log2(-i))));
                     }
                 }
             }
 
             if (op.isAssociative()) {
                 // canonicalize expressions like "(a * 1) * 2"
-                return reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
+                return reassociate(self != null ? self : (MulNode) new MulNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
             }
         }
-        return this;
+        return self != null ? self : new MulNode(forX, forY).maybeCommuteInputs();
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -49,20 +49,37 @@
         super(TYPE, ArithmeticOpTable::getNeg, value);
     }
 
+    public static ValueNode create(ValueNode value) {
+        ValueNode synonym = findSynonym(value);
+        if (synonym != null) {
+            return synonym;
+        }
+        return new NegateNode(value);
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        ValueNode ret = super.canonical(tool, forValue);
-        if (ret != this) {
-            return ret;
+        ValueNode synonym = findSynonym(forValue, getOp(forValue));
+        if (synonym != null) {
+            return synonym;
+        }
+        return this;
+    }
+
+    protected static ValueNode findSynonym(ValueNode forValue) {
+        ArithmeticOpTable.UnaryOp<Neg> negOp = ArithmeticOpTable.forStamp(forValue.stamp()).getNeg();
+        ValueNode synonym = UnaryArithmeticNode.findSynonym(forValue, negOp);
+        if (synonym != null) {
+            return synonym;
         }
         if (forValue instanceof NegateNode) {
             return ((NegateNode) forValue).getValue();
         }
         if (forValue instanceof SubNode && !(forValue.stamp() instanceof FloatStamp)) {
             SubNode sub = (SubNode) forValue;
-            return new SubNode(sub.getY(), sub.getX());
+            return SubNode.create(sub.getY(), sub.getX());
         }
-        return this;
+        return null;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -93,11 +93,11 @@
         LogicNode equalComp;
         LogicNode lessComp;
         if (getX().stamp() instanceof FloatStamp) {
-            equalComp = graph().unique(FloatEqualsNode.create(getX(), getY(), tool.getConstantReflection()));
-            lessComp = graph().unique(FloatLessThanNode.create(getX(), getY(), isUnorderedLess, tool.getConstantReflection()));
+            equalComp = graph().addOrUniqueWithInputs(FloatEqualsNode.create(getX(), getY()));
+            lessComp = graph().addOrUniqueWithInputs(FloatLessThanNode.create(getX(), getY(), isUnorderedLess));
         } else {
-            equalComp = graph().unique(IntegerEqualsNode.create(getX(), getY(), tool.getConstantReflection()));
-            lessComp = graph().unique(IntegerLessThanNode.create(getX(), getY(), tool.getConstantReflection()));
+            equalComp = graph().addOrUniqueWithInputs(IntegerEqualsNode.create(getX(), getY()));
+            lessComp = graph().addOrUniqueWithInputs(IntegerLessThanNode.create(getX(), getY()));
         }
 
         ConditionalNode equalValue = graph().unique(new ConditionalNode(equalComp, ConstantNode.forInt(0, graph()), ConstantNode.forInt(1, graph())));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -56,9 +56,8 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new OrNode(x, y).maybeCommuteInputs();
         }
+        return canonical(null, op, stamp, x, y);
     }
 
     @Override
@@ -68,6 +67,10 @@
             return ret;
         }
 
+        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
@@ -76,20 +79,23 @@
         }
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
-            if (getOp(forX, forY).isNeutral(c)) {
+            if (op.isNeutral(c)) {
                 return forX;
             }
 
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long rawY = ((PrimitiveConstant) c).asLong();
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp()));
+                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp));
                 if ((rawY & mask) == mask) {
-                    return ConstantNode.forIntegerStamp(stamp(), mask);
+                    return ConstantNode.forIntegerStamp(stamp, mask);
                 }
             }
-            return reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
         }
-        return this;
+        if (forX instanceof NotNode && forY instanceof NotNode) {
+            return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue()));
+        }
+        return self != null ? self : new OrNode(forX, forY).maybeCommuteInputs();
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,7 @@
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shr;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
@@ -44,6 +45,17 @@
         super(TYPE, ArithmeticOpTable::getShr, x, y);
     }
 
+    public static ValueNode create(ValueNode x, ValueNode y) {
+        ArithmeticOpTable.ShiftOp<Shr> op = ArithmeticOpTable.forStamp(x.stamp()).getShr();
+        Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp());
+        ValueNode value = ShiftNode.canonical(op, stamp, x, y);
+        if (value != null) {
+            return value;
+        }
+
+        return canonical(null, op, stamp, x, y);
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode ret = super.canonical(tool, forX, forY);
@@ -51,6 +63,11 @@
             return ret;
         }
 
+        return canonical(this, getArithmeticOp(), stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTable.ShiftOp<Shr> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+        RightShiftNode self = rightShiftNode;
         if (forX.stamp() instanceof IntegerStamp && ((IntegerStamp) forX.stamp()).isPositive()) {
             return new UnsignedRightShiftNode(forX, forY);
         }
@@ -58,7 +75,7 @@
         if (forY.isConstant()) {
             int amount = forY.asJavaConstant().asInt();
             int originalAmout = amount;
-            int mask = getShiftAmountMask();
+            int mask = op.getShiftAmountMask(stamp);
             amount &= mask;
             if (amount == 0) {
                 return forX;
@@ -74,10 +91,10 @@
                             IntegerStamp istamp = (IntegerStamp) other.getX().stamp();
 
                             if (istamp.isPositive()) {
-                                return ConstantNode.forIntegerKind(getStackKind(), 0);
+                                return ConstantNode.forIntegerKind(stamp.getStackKind(), 0);
                             }
                             if (istamp.isStrictlyNegative()) {
-                                return ConstantNode.forIntegerKind(getStackKind(), -1L);
+                                return ConstantNode.forIntegerKind(stamp.getStackKind(), -1L);
                             }
 
                             /*
@@ -95,7 +112,10 @@
                 return new RightShiftNode(forX, ConstantNode.forInt(amount));
             }
         }
-        return this;
+        if (self == null) {
+            self = new RightShiftNode(forX, forY);
+        }
+        return self;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -85,12 +85,20 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        ValueNode valueNode = canonical(getOp(forX), stamp(), forX, forY);
+        if (valueNode != null) {
+            return valueNode;
+        }
+        return this;
+    }
+
+    public static <OP> ValueNode canonical(ShiftOp<OP> op, Stamp stamp, ValueNode forX, ValueNode forY) {
         if (forX.isConstant() && forY.isConstant()) {
             JavaConstant amount = forY.asJavaConstant();
             assert amount.getJavaKind() == JavaKind.Int;
-            return ConstantNode.forPrimitive(stamp(), getOp(forX).foldConstant(forX.asConstant(), amount.asInt()));
+            return ConstantNode.forPrimitive(stamp, op.foldConstant(forX.asConstant(), amount.asInt()));
         }
-        return this;
+        return null;
     }
 
     public int getShiftAmountMask() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -63,9 +63,8 @@
         ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp()));
         if (synonym != null) {
             return synonym;
-        } else {
-            return new SignExtendNode(input, inputBits, resultBits);
         }
+        return canonical(null, input, inputBits, resultBits);
     }
 
     @Override
@@ -80,30 +79,34 @@
             return ret;
         }
 
+        return canonical(this, forValue, getInputBits(), getResultBits());
+    }
+
+    private static ValueNode canonical(SignExtendNode self, ValueNode forValue, int inputBits, int resultBits) {
         if (forValue instanceof SignExtendNode) {
             // sxxx -(sign-extend)-> ssss sxxx -(sign-extend)-> ssssssss sssssxxx
             // ==> sxxx -(sign-extend)-> ssssssss sssssxxx
             SignExtendNode other = (SignExtendNode) forValue;
-            return SignExtendNode.create(other.getValue(), other.getInputBits(), getResultBits());
+            return SignExtendNode.create(other.getValue(), other.getInputBits(), resultBits);
         } else if (forValue instanceof ZeroExtendNode) {
             ZeroExtendNode other = (ZeroExtendNode) forValue;
             if (other.getResultBits() > other.getInputBits()) {
                 // sxxx -(zero-extend)-> 0000 sxxx -(sign-extend)-> 00000000 0000sxxx
                 // ==> sxxx -(zero-extend)-> 00000000 0000sxxx
-                return ZeroExtendNode.create(other.getValue(), other.getInputBits(), getResultBits());
+                return ZeroExtendNode.create(other.getValue(), other.getInputBits(), resultBits);
             }
         }
 
         if (forValue.stamp() instanceof IntegerStamp) {
             IntegerStamp inputStamp = (IntegerStamp) forValue.stamp();
-            if ((inputStamp.upMask() & (1L << (getInputBits() - 1))) == 0L) {
+            if ((inputStamp.upMask() & (1L << (inputBits - 1))) == 0L) {
                 // 0xxx -(sign-extend)-> 0000 0xxx
                 // ==> 0xxx -(zero-extend)-> 0000 0xxx
-                return ZeroExtendNode.create(forValue, getInputBits(), getResultBits());
+                return ZeroExtendNode.create(forValue, inputBits, resultBits);
             }
         }
 
-        return this;
+        return self != null ? self : new SignExtendNode(forValue, inputBits, resultBits);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -67,7 +67,7 @@
                 return forX;
             }
             if (c == -1) {
-                return new NegateNode(forX);
+                return NegateNode.create(forX);
             }
             long abs = Math.abs(c);
             if (CodeUtil.isPowerOf2(abs) && forX.stamp() instanceof IntegerStamp) {
@@ -83,7 +83,7 @@
                 }
                 RightShiftNode shift = new RightShiftNode(dividend, ConstantNode.forInt(log2));
                 if (c < 0) {
-                    return new NegateNode(shift);
+                    return NegateNode.create(shift);
                 }
                 return shift;
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -59,24 +59,16 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new SubNode(x, y);
         }
+        return canonical(null, op, stamp, x, y);
     }
 
-    @SuppressWarnings("hiding")
-    @Override
-    public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode ret = super.canonical(tool, forX, forY);
-        if (ret != this) {
-            return ret;
-        }
-
-        BinaryOp<Sub> op = getOp(forX, forY);
+    private static ValueNode canonical(SubNode subNode, BinaryOp<Sub> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+        SubNode self = subNode;
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             Constant zero = op.getZero(forX.stamp());
             if (zero != null) {
-                return ConstantNode.forPrimitive(stamp(), zero);
+                return ConstantNode.forPrimitive(stamp, zero);
             }
         }
         boolean associative = op.isAssociative();
@@ -95,18 +87,18 @@
                 SubNode x = (SubNode) forX;
                 if (x.getX() == forY) {
                     // (a - b) - a
-                    return new NegateNode(x.getY());
+                    return NegateNode.create(x.getY());
                 }
             }
             if (forY instanceof AddNode) {
                 AddNode y = (AddNode) forY;
                 if (y.getX() == forX) {
                     // a - (a + b)
-                    return new NegateNode(y.getY());
+                    return NegateNode.create(y.getY());
                 }
                 if (y.getY() == forX) {
                     // b - (a + b)
-                    return new NegateNode(y.getX());
+                    return NegateNode.create(y.getX());
                 }
             } else if (forY instanceof SubNode) {
                 SubNode y = (SubNode) forY;
@@ -121,9 +113,9 @@
             if (op.isNeutral(c)) {
                 return forX;
             }
-            if (associative) {
-                ValueNode reassociated = reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
-                if (reassociated != this) {
+            if (associative && self != null) {
+                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
+                if (reassociated != self) {
                     return reassociated;
                 }
             }
@@ -132,27 +124,41 @@
                 if (i < 0 || ((IntegerStamp) StampFactory.forKind(forY.getStackKind())).contains(-i)) {
                     // Adding a negative is more friendly to the backend since adds are
                     // commutative, so prefer add when it fits.
-                    return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp(), -i));
+                    return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp, -i));
                 }
             }
         } else if (forX.isConstant()) {
             Constant c = forX.asConstant();
-            if (ArithmeticOpTable.forStamp(stamp()).getAdd().isNeutral(c)) {
+            if (ArithmeticOpTable.forStamp(stamp).getAdd().isNeutral(c)) {
                 /*
                  * Note that for floating point numbers, + and - have different neutral elements. We
                  * have to test for the neutral element of +, because we are doing this
                  * transformation: 0 - x == (-x) + 0 == -x.
                  */
-                return new NegateNode(forY);
+                return NegateNode.create(forY);
             }
-            if (associative) {
-                return reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
+            if (associative && self != null) {
+                return reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
             }
         }
         if (forY instanceof NegateNode) {
             return BinaryArithmeticNode.add(forX, ((NegateNode) forY).getValue());
         }
-        return this;
+        if (self == null) {
+            self = new SubNode(forX, forY);
+        }
+        return self;
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        ValueNode ret = super.canonical(tool, forX, forY);
+        if (ret != this) {
+            return ret;
+        }
+
+        BinaryOp<Sub> op = getOp(forX, forY);
+        return canonical(this, op, stamp, forX, forY);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -57,9 +57,8 @@
         ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
         if (tryConstantFold != null) {
             return tryConstantFold;
-        } else {
-            return new XorNode(x, y).maybeCommuteInputs();
         }
+        return canonical(null, op, stamp, x, y);
     }
 
     @Override
@@ -69,28 +68,32 @@
             return ret;
         }
 
+        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+    }
+
+    private static ValueNode canonical(XorNode self, BinaryOp<Xor> op, Stamp stamp, ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
-            return ConstantNode.forPrimitive(stamp(), getOp(forX, forY).getZero(forX.stamp()));
+            return ConstantNode.forPrimitive(stamp, op.getZero(forX.stamp()));
         }
         if (forX.isConstant() && !forY.isConstant()) {
             return new XorNode(forY, forX);
         }
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
-            if (getOp(forX, forY).isNeutral(c)) {
+            if (op.isNeutral(c)) {
                 return forX;
             }
 
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long rawY = ((PrimitiveConstant) c).asLong();
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp()));
+                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp));
                 if ((rawY & mask) == mask) {
                     return new NotNode(forX);
                 }
             }
-            return reassociate(this, ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (XorNode) new XorNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
         }
-        return this;
+        return self != null ? self : new XorNode(forX, forY).maybeCommuteInputs();
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -67,9 +67,8 @@
         ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp()));
         if (synonym != null) {
             return synonym;
-        } else {
-            return new ZeroExtendNode(input, inputBits, resultBits);
         }
+        return canonical(null, input, inputBits, resultBits);
     }
 
     @Override
@@ -97,11 +96,16 @@
             return ret;
         }
 
+        return canonical(this, forValue, getInputBits(), getResultBits());
+    }
+
+    private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits) {
+        ZeroExtendNode self = zeroExtendNode;
         if (forValue instanceof ZeroExtendNode) {
             // xxxx -(zero-extend)-> 0000 xxxx -(zero-extend)-> 00000000 0000xxxx
             // ==> xxxx -(zero-extend)-> 00000000 0000xxxx
             ZeroExtendNode other = (ZeroExtendNode) forValue;
-            return new ZeroExtendNode(other.getValue(), other.getInputBits(), getResultBits());
+            return new ZeroExtendNode(other.getValue(), other.getInputBits(), resultBits);
         }
         if (forValue instanceof NarrowNode) {
             NarrowNode narrow = (NarrowNode) forValue;
@@ -128,7 +132,10 @@
             }
         }
 
-        return this;
+        if (self == null) {
+            self = new ZeroExtendNode(forValue, inputBits, resultBits);
+        }
+        return self;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Thu Apr 06 14:31:32 2017 -0700
@@ -607,7 +607,7 @@
                                 if (sux.loop != loop) {
                                     AbstractBeginNode begin = sux.getBeginNode();
                                     if (!(begin instanceof LoopExitNode && ((LoopExitNode) begin).loopBegin() == loopBegin)) {
-                                        Debug.log(Debug.VERBOSE_LOG_LEVEL, "Unexpected loop exit with %s, including whole branch in the loop", sux);
+                                        Debug.log(Debug.VERBOSE_LEVEL, "Unexpected loop exit with %s, including whole branch in the loop", sux);
                                         computeLoopBlocks(sux, loop, stack, false);
                                     }
                                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -97,23 +97,22 @@
             }
             boolean usageFound = false;
             for (IntegerEqualsNode node : this.usages().filter(IntegerEqualsNode.class)) {
-                if (node.condition() == Condition.EQ) {
-                    ValueNode other = node.getX();
-                    if (node.getX() == this) {
-                        other = node.getY();
+                assert node.condition() == Condition.EQ;
+                ValueNode other = node.getX();
+                if (node.getX() == this) {
+                    other = node.getY();
+                }
+                if (other.isConstant()) {
+                    double probabilityToSet = probabilityValue;
+                    if (other.asJavaConstant().asInt() == 0) {
+                        probabilityToSet = 1.0 - probabilityToSet;
                     }
-                    if (other.isConstant()) {
-                        double probabilityToSet = probabilityValue;
-                        if (other.asJavaConstant().asInt() == 0) {
-                            probabilityToSet = 1.0 - probabilityToSet;
-                        }
-                        for (IfNode ifNodeUsages : node.usages().filter(IfNode.class)) {
-                            usageFound = true;
-                            ifNodeUsages.setTrueSuccessorProbability(probabilityToSet);
-                        }
-                        if (!usageFound) {
-                            usageFound = node.usages().filter(NodePredicates.isA(FixedGuardNode.class).or(ConditionalNode.class)).isNotEmpty();
-                        }
+                    for (IfNode ifNodeUsages : node.usages().filter(IfNode.class)) {
+                        usageFound = true;
+                        ifNodeUsages.setTrueSuccessorProbability(probabilityToSet);
+                    }
+                    if (!usageFound) {
+                        usageFound = node.usages().filter(NodePredicates.isA(FixedGuardNode.class).or(ConditionalNode.class)).isNotEmpty();
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -194,7 +194,7 @@
         } else {
             int newDefaultSuccessor = addNewSuccessor(defaultSuccessor(), newSuccessors);
             double newDefaultProbability = keyProbabilities[keyProbabilities.length - 1];
-            doReplace(tool, value(), newKeyDatas, newSuccessors, newDefaultSuccessor, newDefaultProbability);
+            doReplace(value(), newKeyDatas, newSuccessors, newDefaultSuccessor, newDefaultProbability);
             return true;
         }
     }
@@ -294,7 +294,7 @@
          * Build the low-level representation of the new switch keys and replace ourself with a new
          * node.
          */
-        doReplace(tool, newValue, newKeyDatas, newSuccessors, newDefaultSuccessor, newDefaultProbability);
+        doReplace(newValue, newKeyDatas, newSuccessors, newDefaultSuccessor, newDefaultProbability);
 
         /* The array load is now unnecessary. */
         assert loadIndexed.hasNoUsages();
@@ -312,7 +312,7 @@
         return index;
     }
 
-    private void doReplace(SimplifierTool tool, ValueNode newValue, List<KeyData> newKeyDatas, ArrayList<AbstractBeginNode> newSuccessors, int newDefaultSuccessor, double newDefaultProbability) {
+    private void doReplace(ValueNode newValue, List<KeyData> newKeyDatas, ArrayList<AbstractBeginNode> newSuccessors, int newDefaultSuccessor, double newDefaultProbability) {
         /* Sort the new keys (invariant of the IntegerSwitchNode). */
         newKeyDatas.sort((k1, k2) -> k1.key - k2.key);
 
@@ -353,7 +353,7 @@
             if (!newSuccessors.contains(successor)) {
                 FixedNode fixedBranch = successor;
                 fixedBranch.predecessor().replaceFirstSuccessor(fixedBranch, null);
-                GraphUtil.killCFG(fixedBranch, tool);
+                GraphUtil.killCFG(fixedBranch);
             }
             setBlockSuccessor(i, null);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -27,6 +27,7 @@
 
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.type.PrimitiveStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -52,10 +53,21 @@
 public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtualizable {
     public static final NodeClass<RawLoadNode> TYPE = NodeClass.create(RawLoadNode.class);
 
+    /**
+     * This constructor exists for node intrinsics that need a stamp based on {@code accessKind}.
+     */
     public RawLoadNode(ValueNode object, ValueNode offset, JavaKind accessKind, LocationIdentity locationIdentity) {
         super(TYPE, StampFactory.forKind(accessKind.getStackKind()), object, offset, accessKind, locationIdentity, false);
     }
 
+    /**
+     * This constructor exists for node intrinsics that need a stamp based on the return type of the
+     * {@link org.graalvm.compiler.graph.Node.NodeIntrinsic} annotated method.
+     */
+    public RawLoadNode(@InjectedNodeParameter Stamp stamp, ValueNode object, ValueNode offset, LocationIdentity locationIdentity, JavaKind accessKind) {
+        super(TYPE, stamp, object, offset, accessKind, locationIdentity, false);
+    }
+
     public RawLoadNode(NodeClass<? extends RawLoadNode> c, ValueNode object, ValueNode offset, JavaKind accessKind, LocationIdentity locationIdentity) {
         super(c, StampFactory.forKind(accessKind.getStackKind()), object, offset, accessKind, locationIdentity, false);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeCopyNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeCopyNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,15 +22,15 @@
  */
 package org.graalvm.compiler.nodes.extended;
 
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
 /**
  * Copy a value at a location specified as an offset relative to a source object to another location
  * specified as an offset relative to destination object. No null checks are performed.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java	Thu Apr 06 14:31:32 2017 -0700
@@ -70,8 +70,9 @@
     void push(JavaKind kind, ValueNode value);
 
     /**
-     * Adds a node to the graph. If the returned node is a {@link StateSplit} with a null
-     * {@linkplain StateSplit#stateAfter() frame state}, the frame state is initialized.
+     * Adds a node to the graph. If the node is in the graph, returns immediately. If the node is a
+     * {@link StateSplit} with a null {@linkplain StateSplit#stateAfter() frame state}, the frame
+     * state is initialized.
      *
      * @param value the value to add to the graph and push to the stack. The
      *            {@code value.getJavaKind()} kind is used when type checking this operation.
@@ -92,6 +93,30 @@
         return equivalentValue;
     }
 
+    /**
+     * Adds a node and its inputs to the graph. If the node is in the graph, returns immediately. If
+     * the node is a {@link StateSplit} with a null {@linkplain StateSplit#stateAfter() frame state}
+     * , the frame state is initialized.
+     *
+     * @param value the value to add to the graph and push to the stack. The
+     *            {@code value.getJavaKind()} kind is used when type checking this operation.
+     * @return a node equivalent to {@code value} in the graph
+     */
+    default <T extends ValueNode> T addWithInputs(T value) {
+        if (value.graph() != null) {
+            assert !(value instanceof StateSplit) || ((StateSplit) value).stateAfter() != null;
+            return value;
+        }
+        T equivalentValue = recursiveAppend(value);
+        if (equivalentValue instanceof StateSplit) {
+            StateSplit stateSplit = (StateSplit) equivalentValue;
+            if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) {
+                setStateAfter(stateSplit);
+            }
+        }
+        return equivalentValue;
+    }
+
     default ValueNode addNonNullCast(ValueNode value) {
         AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp();
         if (valueStamp.nonNull()) {
@@ -100,7 +125,7 @@
             LogicNode isNull = add(IsNullNode.create(value));
             FixedGuardNode fixedGuard = add(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, true));
             Stamp newStamp = valueStamp.improveWith(StampFactory.objectNonNull());
-            return add(new PiNode(value, newStamp, fixedGuard));
+            return add(PiNode.create(value, newStamp, fixedGuard));
         }
     }
 
@@ -245,12 +270,12 @@
      * non-null} stamp.
      */
     default ValueNode nullCheckedValue(ValueNode value, DeoptimizationAction action) {
-        if (!StampTool.isPointerNonNull(value.stamp())) {
+        if (!StampTool.isPointerNonNull(value)) {
             LogicNode condition = getGraph().unique(IsNullNode.create(value));
             ObjectStamp receiverStamp = (ObjectStamp) value.stamp();
             Stamp stamp = receiverStamp.join(objectNonNull());
             FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, action, true));
-            PiNode nonNullReceiver = getGraph().unique(new PiNode(value, stamp, fixedGuard));
+            ValueNode nonNullReceiver = getGraph().addOrUnique(PiNode.create(value, stamp, fixedGuard));
             // TODO: Propogating the non-null into the frame state would
             // remove subsequent null-checks on the same value. However,
             // it currently causes an assertion failure when merging states.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/NodeIntrinsicPluginFactory.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/NodeIntrinsicPluginFactory.java	Thu Apr 06 14:31:32 2017 -0700
@@ -30,7 +30,14 @@
 
         <T> T getInjectedArgument(Class<T> type);
 
-        Stamp getReturnStamp(Class<?> type, boolean nonNull);
+        /**
+         * Gets a stamp denoting a given type and non-nullness property.
+         *
+         * @param type the type the returned stamp represents
+         * @param nonNull specifies if the returned stamp denotes a value that is guaranteed to be
+         *            non-null
+         */
+        Stamp getInjectedStamp(Class<?> type, boolean nonNull);
     }
 
     void registerPlugins(InvocationPlugins plugins, InjectionProvider injection);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -24,6 +24,8 @@
 
 import static org.graalvm.compiler.graph.iterators.NodePredicates.isNotA;
 
+import jdk.vm.ci.meta.ConstantReflectionProvider;
+import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
@@ -51,6 +53,7 @@
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaField;
+import org.graalvm.compiler.options.OptionValues;
 
 /**
  * The {@code LoadFieldNode} represents a read of a static or instance field.
@@ -71,10 +74,21 @@
         return new LoadFieldNode(StampFactory.forDeclaredType(assumptions, field.getType(), false), object, field);
     }
 
+    public static ValueNode create(ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess,
+                    OptionValues options, Assumptions assumptions, ValueNode object, ResolvedJavaField field, boolean canonicalizeReads, boolean allUsagesAvailable) {
+        return canonical(null, StampFactory.forDeclaredType(assumptions, field.getType(), false), object,
+                        field, constantFields, constantReflection, options, metaAccess, canonicalizeReads, allUsagesAvailable);
+    }
+
     public static LoadFieldNode createOverrideStamp(StampPair stamp, ValueNode object, ResolvedJavaField field) {
         return new LoadFieldNode(stamp, object, field);
     }
 
+    public static ValueNode createOverrideStamp(ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess,
+                    OptionValues options, StampPair stamp, ValueNode object, ResolvedJavaField field, boolean canonicalizeReads, boolean allUsagesAvailable) {
+        return canonical(null, stamp, object, field, constantFields, constantReflection, options, metaAccess, canonicalizeReads, allUsagesAvailable);
+    }
+
     @Override
     public ValueNode getValue() {
         return object();
@@ -85,33 +99,50 @@
         if (tool.allUsagesAvailable() && hasNoUsages() && !isVolatile() && (isStatic() || StampTool.isPointerNonNull(forObject.stamp()))) {
             return null;
         }
-        MetaAccessProvider metaAccess = tool.getMetaAccess();
-        if (tool.canonicalizeReads() && metaAccess != null) {
-            ConstantNode constant = asConstant(tool, forObject);
+        return canonical(this, StampPair.create(stamp, uncheckedStamp), forObject, field, tool.getConstantFieldProvider(),
+                        tool.getConstantReflection(), tool.getOptions(), tool.getMetaAccess(), tool.canonicalizeReads(), tool.allUsagesAvailable());
+    }
+
+    private static ValueNode canonical(LoadFieldNode loadFieldNode, StampPair stamp, ValueNode forObject, ResolvedJavaField field,
+                    ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection,
+                    OptionValues options, MetaAccessProvider metaAccess, boolean canonicalizeReads, boolean allUsagesAvailable) {
+        LoadFieldNode self = loadFieldNode;
+        if (canonicalizeReads && metaAccess != null) {
+            ConstantNode constant = asConstant(constantFields, constantReflection, metaAccess, options, forObject, field);
             if (constant != null) {
                 return constant;
             }
-            if (tool.allUsagesAvailable()) {
-                PhiNode phi = asPhi(tool, forObject);
+            if (allUsagesAvailable) {
+                PhiNode phi = asPhi(constantFields, constantReflection, metaAccess, options, forObject,
+                                field, stamp.getTrustedStamp());
                 if (phi != null) {
                     return phi;
                 }
             }
         }
-        if (!isStatic() && forObject.isNullConstant()) {
+        if (self != null && !field.isStatic() && forObject.isNullConstant()) {
             return new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException);
         }
-        return this;
+        if (self == null) {
+            self = new LoadFieldNode(stamp, forObject, field);
+        }
+        return self;
     }
 
     /**
      * Gets a constant value for this load if possible.
      */
     public ConstantNode asConstant(CanonicalizerTool tool, ValueNode forObject) {
-        if (isStatic()) {
-            return ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), null, getOptions());
+        return asConstant(tool.getConstantFieldProvider(), tool.getConstantReflection(),
+                        tool.getMetaAccess(), tool.getOptions(), forObject, field);
+    }
+
+    private static ConstantNode asConstant(ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection,
+                    MetaAccessProvider metaAccess, OptionValues options, ValueNode forObject, ResolvedJavaField field) {
+        if (field.isStatic()) {
+            return ConstantFoldUtil.tryConstantFold(constantFields, constantReflection, metaAccess, field, null, options);
         } else if (forObject.isConstant() && !forObject.isNullConstant()) {
-            return ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), forObject.asJavaConstant(), getOptions());
+            return ConstantFoldUtil.tryConstantFold(constantFields, constantReflection, metaAccess, field, forObject.asJavaConstant(), options);
         }
         return null;
     }
@@ -120,19 +151,20 @@
         return ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), constant, getOptions());
     }
 
-    private PhiNode asPhi(CanonicalizerTool tool, ValueNode forObject) {
-        if (!isStatic() && field.isFinal() && forObject instanceof ValuePhiNode && ((ValuePhiNode) forObject).values().filter(isNotA(ConstantNode.class)).isEmpty()) {
+    private static PhiNode asPhi(ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection,
+                    MetaAccessProvider metaAcccess, OptionValues options, ValueNode forObject, ResolvedJavaField field, Stamp stamp) {
+        if (!field.isStatic() && field.isFinal() && forObject instanceof ValuePhiNode && ((ValuePhiNode) forObject).values().filter(isNotA(ConstantNode.class)).isEmpty()) {
             PhiNode phi = (PhiNode) forObject;
             ConstantNode[] constantNodes = new ConstantNode[phi.valueCount()];
             for (int i = 0; i < phi.valueCount(); i++) {
-                ConstantNode constant = ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), phi.valueAt(i).asJavaConstant(),
-                                getOptions());
+                ConstantNode constant = ConstantFoldUtil.tryConstantFold(constantFields, constantReflection, metaAcccess, field, phi.valueAt(i).asJavaConstant(),
+                                options);
                 if (constant == null) {
                     return null;
                 }
                 constantNodes[i] = constant;
             }
-            return new ValuePhiNode(stamp(), phi.merge(), constantNodes);
+            return new ValuePhiNode(stamp, phi.merge(), constantNodes);
         }
         return null;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -239,8 +239,8 @@
                 LogicNode condition = graph().addOrUniqueWithInputs(InstanceOfNode.create(speculatedType, receiver, getProfile(), anchor));
                 FixedGuardNode guard = graph().add(new FixedGuardNode(condition, DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile, false));
                 graph().addBeforeFixed(invoke().asNode(), guard);
-                PiNode piNode = graph().unique(new PiNode(receiver, StampFactory.objectNonNull(speculatedType), guard));
-                arguments().set(0, piNode);
+                ValueNode valueNode = graph().addOrUnique(new PiNode(receiver, StampFactory.objectNonNull(speculatedType), guard));
+                arguments().set(0, valueNode);
                 if (speculatedType.isExact()) {
                     setInvokeKind(InvokeKind.Special);
                 } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorIdNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorIdNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -45,6 +45,7 @@
 
     public static final NodeClass<MonitorIdNode> TYPE = NodeClass.create(MonitorIdNode.class);
     protected int lockDepth;
+    protected boolean eliminated;
 
     public MonitorIdNode(int lockDepth) {
         this(TYPE, lockDepth);
@@ -63,6 +64,14 @@
         this.lockDepth = lockDepth;
     }
 
+    public boolean isEliminated() {
+        return eliminated;
+    }
+
+    public void setEliminated() {
+        eliminated = true;
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool generator) {
         // nothing to do
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/NodeValueMap.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/NodeValueMap.java	Thu Apr 06 14:31:32 2017 -0700
@@ -53,7 +53,7 @@
     Value setResult(ValueNode node, Value operand);
 
     /**
-     * Gets the the {@link ValueNode} that produced a {@code value}. If the {@code value} is not
+     * Gets the {@link ValueNode} that produced a {@code value}. If the {@code value} is not
      * associated with a {@link ValueNode} {@code null} is returned.
      *
      * This method is intended for debugging purposes only.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java	Thu Apr 06 14:31:32 2017 -0700
@@ -78,7 +78,7 @@
     public static Stamp unsignedCompare(Stamp stamp, Stamp stamp2) {
         IntegerStamp x = (IntegerStamp) stamp;
         IntegerStamp y = (IntegerStamp) stamp2;
-        if (x == x.unrestricted() && y == y.unrestricted()) {
+        if (x.isUnrestricted() && y.isUnrestricted()) {
             // Don't know anything.
             return null;
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,21 +32,19 @@
 import org.graalvm.compiler.code.SourceStackTraceBailoutException;
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
-import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
-import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeSourcePosition;
+import org.graalvm.compiler.graph.NodeStack;
 import org.graalvm.compiler.graph.NodeWorkList;
 import org.graalvm.compiler.graph.Position;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
-import org.graalvm.compiler.nodeinfo.InputType;
-import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
+import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.FrameState;
@@ -60,7 +58,6 @@
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
-import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
 import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
 import org.graalvm.compiler.nodes.spi.LimitedValueProxy;
@@ -70,8 +67,10 @@
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
+import org.graalvm.util.MapCursor;
 
 import jdk.vm.ci.code.BailoutException;
 import jdk.vm.ci.code.BytecodePosition;
@@ -88,14 +87,142 @@
         public static final OptionKey<Boolean> VerifyKillCFGUnusedNodes = new OptionKey<>(false);
     }
 
+    private static void killCFGInner(FixedNode node) {
+        EconomicSet<Node> markedNodes = EconomicSet.create();
+        EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges = EconomicMap.create();
+
+        // Detach this node from CFG
+        node.replaceAtPredecessor(null);
+
+        markFixedNodes(node, markedNodes, unmarkedMerges);
+
+        fixSurvivingAffectedMerges(markedNodes, unmarkedMerges);
+
+        Debug.dump(Debug.DETAILED_LEVEL, node.graph(), "After fixing merges (killCFG %s)", node);
+
+        // Mark non-fixed nodes
+        markUsages(markedNodes);
+
+        // Detach marked nodes from non-marked nodes
+        for (Node marked : markedNodes) {
+            for (Node input : marked.inputs()) {
+                if (!markedNodes.contains(input)) {
+                    marked.replaceFirstInput(input, null);
+                    tryKillUnused(input);
+                }
+            }
+        }
+        Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "After disconnecting non-marked inputs (killCFG %s)", node);
+        // Kill marked nodes
+        for (Node marked : markedNodes) {
+            if (marked.isAlive()) {
+                marked.markDeleted();
+            }
+        }
+    }
+
+    private static void markFixedNodes(FixedNode node, EconomicSet<Node> markedNodes, EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges) {
+        NodeStack workStack = new NodeStack();
+        workStack.push(node);
+        while (!workStack.isEmpty()) {
+            Node fixedNode = workStack.pop();
+            markedNodes.add(fixedNode);
+            if (fixedNode instanceof AbstractMergeNode) {
+                unmarkedMerges.removeKey((AbstractMergeNode) fixedNode);
+            }
+            while (fixedNode instanceof FixedWithNextNode) {
+                fixedNode = ((FixedWithNextNode) fixedNode).next();
+                if (fixedNode != null) {
+                    markedNodes.add(fixedNode);
+                }
+            }
+            if (fixedNode instanceof ControlSplitNode) {
+                for (Node successor : fixedNode.successors()) {
+                    workStack.push(successor);
+                }
+            } else if (fixedNode instanceof AbstractEndNode) {
+                AbstractEndNode end = (AbstractEndNode) fixedNode;
+                AbstractMergeNode merge = end.merge();
+                if (merge != null) {
+                    assert !markedNodes.contains(merge) || (merge instanceof LoopBeginNode && end instanceof LoopEndNode) : merge;
+                    if (merge instanceof LoopBeginNode) {
+                        if (end == ((LoopBeginNode) merge).forwardEnd()) {
+                            workStack.push(merge);
+                            continue;
+                        }
+                        if (markedNodes.contains(merge)) {
+                            continue;
+                        }
+                    }
+                    List<AbstractEndNode> endsSeen = unmarkedMerges.get(merge);
+                    if (endsSeen == null) {
+                        endsSeen = new ArrayList<>(merge.forwardEndCount());
+                        unmarkedMerges.put(merge, endsSeen);
+                    }
+                    endsSeen.add(end);
+                    if (!(end instanceof LoopEndNode) && endsSeen.size() == merge.forwardEndCount()) {
+                        assert merge.forwardEnds().filter(n -> !markedNodes.contains(n)).isEmpty();
+                        // all this merge's forward ends are marked: it needs to be killed
+                        workStack.push(merge);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void fixSurvivingAffectedMerges(EconomicSet<Node> markedNodes, EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges) {
+        MapCursor<AbstractMergeNode, List<AbstractEndNode>> cursor = unmarkedMerges.getEntries();
+        while (cursor.advance()) {
+            AbstractMergeNode merge = cursor.getKey();
+            for (AbstractEndNode end : cursor.getValue()) {
+                merge.removeEnd(end);
+            }
+            if (merge.phiPredecessorCount() == 1) {
+                if (merge instanceof LoopBeginNode) {
+                    LoopBeginNode loopBegin = (LoopBeginNode) merge;
+                    assert merge.forwardEndCount() == 1;
+                    for (LoopExitNode loopExit : loopBegin.loopExits().snapshot()) {
+                        if (markedNodes.contains(loopExit)) {
+                            /*
+                             * disconnect from loop begin so that reduceDegenerateLoopBegin doesn't
+                             * transform it into a new beginNode
+                             */
+                            loopExit.replaceFirstInput(loopBegin, null);
+                        }
+                    }
+                    merge.graph().reduceDegenerateLoopBegin(loopBegin);
+                } else {
+                    merge.graph().reduceTrivialMerge(merge);
+                }
+            } else {
+                assert merge.phiPredecessorCount() > 1 : merge;
+            }
+        }
+    }
+
+    private static void markUsages(EconomicSet<Node> markedNodes) {
+        NodeStack workStack = new NodeStack(markedNodes.size() + 4);
+        for (Node marked : markedNodes) {
+            workStack.push(marked);
+        }
+        while (!workStack.isEmpty()) {
+            Node marked = workStack.pop();
+            for (Node usage : marked.usages()) {
+                if (!markedNodes.contains(usage)) {
+                    workStack.push(usage);
+                    markedNodes.add(usage);
+                }
+            }
+        }
+    }
+
     @SuppressWarnings("try")
-    public static void killCFG(FixedNode node, SimplifierTool tool) {
+    public static void killCFG(FixedNode node) {
         try (Debug.Scope scope = Debug.scope("KillCFG", node)) {
             EconomicSet<Node> unusedNodes = null;
             EconomicSet<Node> unsafeNodes = null;
             Graph.NodeEventScope nodeEventScope = null;
             OptionValues options = node.getOptions();
-            StructuredGraph graph = node.graph();
             if (Graph.Options.VerifyGraalGraphEdges.getValue(options)) {
                 unsafeNodes = collectUnsafeNodes(node.graph());
             }
@@ -110,14 +237,9 @@
                     }
                 });
             }
-            Debug.dump(Debug.VERY_DETAILED_LOG_LEVEL, node.graph(), "Before killCFG %s", node);
-            NodeWorkList worklist = killCFG(node, tool, null);
-            if (worklist != null) {
-                for (Node n : worklist) {
-                    NodeWorkList list = killCFG(n, tool, worklist);
-                    assert list == worklist;
-                }
-            }
+            Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "Before killCFG %s", node);
+            killCFGInner(node);
+            Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "After killCFG %s", node);
             if (Graph.Options.VerifyGraalGraphEdges.getValue(options)) {
                 EconomicSet<Node> newUnsafeNodes = collectUnsafeNodes(node.graph());
                 newUnsafeNodes.removeAll(unsafeNodes);
@@ -133,7 +255,6 @@
                     }
                 }
                 assert unusedNodes.isEmpty() : "New unused nodes: " + unusedNodes;
-                assert graph.getNodes().filter(PoisonNode.class).isEmpty();
             }
         } catch (Throwable t) {
             throw Debug.handle(t);
@@ -158,253 +279,10 @@
         return unsafeNodes;
     }
 
-    private static NodeWorkList killCFG(Node node, SimplifierTool tool, NodeWorkList worklist) {
-        NodeWorkList newWorklist = worklist;
-        if (node instanceof FixedNode) {
-            newWorklist = killCFGLinear((FixedNode) node, newWorklist, tool);
-        } else {
-            if (node instanceof PoisonNode && ((PoisonNode) node).processNonPhiUsagesFirst()) {
-                if (node.isAlive()) {
-                    if (node.hasNoUsages()) {
-                        node.safeDelete();
-                    } else {
-                        if (newWorklist == null) {
-                            newWorklist = node.graph().createIterativeNodeWorkList(false, 0);
-                        }
-                        for (Node usage : node.usages()) {
-                            if (isFloatingNode(usage) && !(usage instanceof PhiNode)) {
-                                newWorklist.add(usage);
-                            }
-                        }
-                        newWorklist.add(node);
-                    }
-                }
-            } else {
-                newWorklist = propagateKill(node, newWorklist);
-                Debug.dump(Debug.VERY_DETAILED_LOG_LEVEL, node.graph(), "killCFG (Floating) %s", node);
-            }
-        }
-        return newWorklist;
-    }
-
-    private static NodeWorkList killCFGLinear(FixedNode in, NodeWorkList worklist, SimplifierTool tool) {
-        NodeWorkList newWorklist = worklist;
-        FixedNode current = in;
-        while (current != null) {
-            FixedNode next = null;
-            assert current.isAlive();
-            if (current instanceof AbstractEndNode) {
-                // We reached a control flow end.
-                AbstractEndNode end = (AbstractEndNode) current;
-                newWorklist = killEnd(end, newWorklist, tool);
-            } else if (current instanceof FixedWithNextNode) {
-                // Node guaranteed to have a single successor
-                FixedWithNextNode fixedWithNext = (FixedWithNextNode) current;
-                assert fixedWithNext.successors().count() == 1 || fixedWithNext.successors().count() == 0;
-                assert fixedWithNext.successors().first() == fixedWithNext.next();
-                next = fixedWithNext.next();
-            } else {
-                /*
-                 * We do not take a successor snapshot because this iterator supports concurrent
-                 * modifications as long as they do not change the size of the successor list. Not
-                 * taking a snapshot allows us to see modifications to other branches that may
-                 * happen while processing one branch.
-                 */
-                Iterator<Node> successors = current.successors().iterator();
-                if (successors.hasNext()) {
-                    Node first = successors.next();
-                    if (!successors.hasNext()) {
-                        next = (FixedNode) first;
-                    } else {
-                        if (newWorklist == null) {
-                            newWorklist = in.graph().createIterativeNodeWorkList(false, 0);
-                        }
-                        for (Node successor : current.successors()) {
-                            newWorklist.add(successor);
-                            if (successor instanceof LoopExitNode) {
-                                LoopExitNode exit = (LoopExitNode) successor;
-                                exit.replaceFirstInput(exit.loopBegin(), null);
-                            }
-                        }
-                    }
-                }
-            }
-            current.replaceAtPredecessor(null);
-            newWorklist = propagateKill(current, newWorklist);
-            Debug.dump(Debug.VERY_DETAILED_LOG_LEVEL, current.graph(), "killCFGLinear %s", current);
-            current = next;
-        }
-        Debug.dump(Debug.DETAILED_LOG_LEVEL, in.graph(), "killCFGLinear %s", in);
-        return newWorklist;
-    }
-
-    public static void killCFG(FixedNode node) {
-        killCFG(node, null);
-    }
-
-    /**
-     * Node type used temporarily while deleting loops.
-     *
-     * It is used as replacement for the loop {@link PhiNode PhiNodes} in order to break data-flow
-     * cycles before deleting the loop. The control-flow of the whole loop is killed before killing
-     * the poison node if they are still alive.
-     */
-    @NodeInfo(allowedUsageTypes = InputType.Unchecked)
-    private static final class PoisonNode extends FloatingNode {
-        public static final NodeClass<PoisonNode> TYPE = NodeClass.create(PoisonNode.class);
-        protected boolean loopPoison;
-
-        protected PoisonNode(boolean loopPoison) {
-            super(TYPE, StampFactory.forVoid());
-            this.loopPoison = loopPoison;
-        }
-
-        public boolean processNonPhiUsagesFirst() {
-            return !this.loopPoison;
-        }
-    }
-
-    private static NodeWorkList killEnd(AbstractEndNode end, NodeWorkList worklist, SimplifierTool tool) {
-        NodeWorkList newWorklist = worklist;
-        AbstractMergeNode merge = end.merge();
-        if (merge != null) {
-            merge.removeEnd(end);
-            StructuredGraph graph = end.graph();
-            if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) {
-                // dead loop
-                LoopBeginNode begin = (LoopBeginNode) merge;
-                // disconnect and delete loop ends & loop exits
-                for (LoopEndNode loopend : begin.loopEnds().snapshot()) {
-                    loopend.predecessor().replaceFirstSuccessor(loopend, null);
-                    loopend.safeDelete();
-                }
-                // clean unused proxies to avoid creating new unused nodes
-                for (LoopExitNode exit : begin.loopExits()) {
-                    for (ProxyNode vpn : exit.proxies().snapshot()) {
-                        tryKillUnused(vpn);
-                    }
-                }
-                begin.removeExits();
-                PoisonNode poison = null;
-                if (merge.phis().isNotEmpty()) {
-                    poison = graph.addWithoutUnique(new PoisonNode(true));
-                    for (PhiNode phi : merge.phis()) {
-                        phi.replaceAtUsages(poison);
-                    }
-                    for (PhiNode phi : merge.phis().snapshot()) {
-                        killWithUnusedFloatingInputs(phi);
-                    }
-                }
-                FixedNode loopBody = begin.next();
-                Debug.dump(Debug.VERY_DETAILED_LOG_LEVEL, end.graph(), "killEnd (Loop) %s after initial loop cleanup", end);
-                if (loopBody != null) {
-                    // for small infinite loops, the body may already be killed while killing the
-                    // LoopEnds
-                    newWorklist = killCFG(loopBody, tool, newWorklist);
-                }
-                FrameState frameState = begin.stateAfter();
-                begin.safeDelete();
-                if (frameState != null) {
-                    tryKillUnused(frameState);
-                }
-                if (poison != null && poison.isAlive()) {
-                    if (newWorklist == null) {
-                        newWorklist = graph.createIterativeNodeWorkList(false, 0);
-                    }
-                    // drain the worklist to finish the loop before adding the poison
-                    List<Node> waitingPoisons = null;
-                    for (Node n : newWorklist) {
-                        if (n instanceof PoisonNode && ((PoisonNode) n).processNonPhiUsagesFirst() && n.hasUsages()) {
-                            assert n != poison;
-                            if (waitingPoisons == null) {
-                                waitingPoisons = new ArrayList<>(2);
-                            }
-                            waitingPoisons.add(n);
-                        } else {
-                            NodeWorkList list = killCFG(n, tool, newWorklist);
-                            assert list == newWorklist;
-                        }
-                    }
-                    if (poison.isAlive()) {
-                        newWorklist.add(poison);
-                    }
-                    if (waitingPoisons != null) {
-                        newWorklist.addAll(waitingPoisons);
-                    }
-                }
-            } else if (merge instanceof LoopBeginNode && ((LoopBeginNode) merge).loopEnds().isEmpty()) {
-                // not a loop anymore
-                if (tool != null) {
-                    for (PhiNode phi : merge.phis()) {
-                        tool.addToWorkList(phi.usages());
-                    }
-                }
-                graph.reduceDegenerateLoopBegin((LoopBeginNode) merge);
-            } else if (merge.phiPredecessorCount() == 1) {
-                // not a merge anymore
-                for (PhiNode phi : merge.phis()) {
-                    if (tool != null) {
-                        tool.addToWorkList(phi.usages());
-                    }
-                    ValueNode value = phi.valueAt(0);
-                    if (value instanceof PoisonNode) {
-                        if (newWorklist == null) {
-                            newWorklist = graph.createIterativeNodeWorkList(false, 0);
-                        }
-                        newWorklist.add(value);
-                    }
-                }
-                graph.reduceTrivialMerge(merge);
-            }
-        }
-        return newWorklist;
-    }
-
     public static boolean isFloatingNode(Node n) {
         return !(n instanceof FixedNode);
     }
 
-    private static NodeWorkList propagateKill(Node node, NodeWorkList workList) {
-        NodeWorkList newWorkList = workList;
-        if (node != null && node.isAlive()) {
-            if (node.hasUsages()) {
-                for (Node usage : node.usages().snapshot()) {
-                    assert usage.isAlive();
-                    if (isFloatingNode(usage)) {
-                        boolean addUsage = false;
-                        if (usage instanceof PhiNode) {
-                            PhiNode phi = (PhiNode) usage;
-                            assert phi.merge() != null;
-                            if (phi.merge() == node) {
-                                // we reach the phi directly through he merge, queue it.
-                                addUsage = true;
-                            } else {
-                                // we reach it though a value
-                                assert phi.values().contains(node);
-                                // let that be handled when we reach the corresponding End node
-                            }
-                        } else {
-                            addUsage = true;
-                        }
-                        if (addUsage) {
-                            if (newWorkList == null) {
-                                newWorkList = node.graph().createIterativeNodeWorkList(false, 0);
-                            }
-                            newWorkList.add(usage);
-                        } else {
-                            usage.replaceFirstInput(node, node.graph().unique(new PoisonNode(false)));
-                            continue;
-                        }
-                    }
-                    usage.replaceFirstInput(node, null);
-                }
-            }
-            assert node.hasNoUsages() : node + " " + node.usages().snapshot();
-            killWithUnusedFloatingInputs(node, true);
-        }
-        return newWorkList;
-    }
-
     private static boolean checkKill(Node node, boolean mayKillGuard) {
         node.assertTrue(mayKillGuard || !(node instanceof GuardNode), "must not be a guard node %s", node);
         node.assertTrue(node.isAlive(), "must be alive");
@@ -972,7 +850,7 @@
         public void deleteBranch(Node branch) {
             FixedNode fixedBranch = (FixedNode) branch;
             fixedBranch.predecessor().replaceFirstSuccessor(fixedBranch, null);
-            GraphUtil.killCFG(fixedBranch, this);
+            GraphUtil.killCFG(fixedBranch);
         }
 
         @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/ModifiableOptionValues.java	Thu Apr 06 14:31:32 2017 -0700
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, 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 org.graalvm.compiler.options;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.Equivalence;
+import org.graalvm.util.UnmodifiableEconomicMap;
+import org.graalvm.util.UnmodifiableMapCursor;
+
+/**
+ * A context for obtaining values for {@link OptionKey}s that allows for key/value pairs to be
+ * updated. Updates have atomic copy-on-write semantics which means a thread may see an old value
+ * when reading but writers will never loose updates.
+ */
+public class ModifiableOptionValues extends OptionValues {
+
+    private final AtomicReference<UnmodifiableEconomicMap<OptionKey<?>, Object>> v = new AtomicReference<>();
+
+    private static final EconomicMap<OptionKey<?>, Object> EMPTY_MAP = newOptionMap();
+
+    public ModifiableOptionValues(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
+        super(EMPTY_MAP);
+        EconomicMap<OptionKey<?>, Object> map = newOptionMap();
+        initMap(map, values);
+        v.set(map);
+    }
+
+    /**
+     * Updates this object with the given key/value pair.
+     */
+    public void update(OptionKey<?> key, Object value) {
+        UnmodifiableEconomicMap<OptionKey<?>, Object> expect;
+        EconomicMap<OptionKey<?>, Object> newMap;
+        do {
+            expect = v.get();
+            newMap = EconomicMap.create(Equivalence.IDENTITY, expect);
+            key.update(newMap, value);
+            // Need to do the null encoding here as `key.update()` doesn't do it
+            newMap.put(key, encodeNull(value));
+        } while (!v.compareAndSet(expect, newMap));
+    }
+
+    /**
+     * Updates this object with the key/value pairs in {@code values}.
+     */
+    public void update(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
+        if (values.isEmpty()) {
+            return;
+        }
+        UnmodifiableEconomicMap<OptionKey<?>, Object> expect;
+        EconomicMap<OptionKey<?>, Object> newMap;
+        do {
+            expect = v.get();
+            newMap = EconomicMap.create(Equivalence.IDENTITY, expect);
+            UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries();
+            while (cursor.advance()) {
+                OptionKey<?> key = cursor.getKey();
+                Object value = cursor.getValue();
+                key.update(newMap, value);
+                // Need to do the null encoding here as `key.update()` doesn't do it
+                newMap.put(key, encodeNull(value));
+            }
+        } while (!v.compareAndSet(expect, newMap));
+    }
+
+    @Override
+    protected <T> T get(OptionKey<T> key) {
+        return OptionValues.get(v.get(), key);
+    }
+
+    @Override
+    protected boolean containsKey(OptionKey<?> key) {
+        return v.get().containsKey(key);
+    }
+
+    @Override
+    public UnmodifiableEconomicMap<OptionKey<?>, Object> getMap() {
+        return v.get();
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java	Thu Apr 06 14:31:32 2017 -0700
@@ -33,7 +33,6 @@
 
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.MapCursor;
 import org.graalvm.util.UnmodifiableEconomicMap;
 import org.graalvm.util.UnmodifiableMapCursor;
 
@@ -42,25 +41,19 @@
  */
 public class OptionValues {
 
-    private final EconomicMap<OptionKey<?>, Object> values = newOptionMap();
+    private final UnmodifiableEconomicMap<OptionKey<?>, Object> values;
 
-    protected OptionValues set(OptionKey<?> key, Object value) {
-        values.put(key, encodeNull(value));
-        return this;
-    }
-
-    boolean containsKey(OptionKey<?> key) {
+    protected boolean containsKey(OptionKey<?> key) {
         return values.containsKey(key);
     }
 
     public OptionValues(OptionValues initialValues, UnmodifiableEconomicMap<OptionKey<?>, Object> extraPairs) {
+        EconomicMap<OptionKey<?>, Object> map = newOptionMap();
         if (initialValues != null) {
-            values.putAll(initialValues.values);
+            map.putAll(initialValues.values);
         }
-        UnmodifiableMapCursor<OptionKey<?>, Object> cursor = extraPairs.getEntries();
-        while (cursor.advance()) {
-            values.put(cursor.getKey(), encodeNull(cursor.getValue()));
-        }
+        initMap(map, extraPairs);
+        this.values = map;
     }
 
     public OptionValues(OptionValues initialValues, OptionKey<?> key1, Object value1, Object... extraPairs) {
@@ -75,7 +68,8 @@
     }
 
     /**
-     * Gets an immutable view of the key/value pairs in this object.
+     * Gets an immutable view of the key/value pairs in this object. Values read from this view
+     * should be {@linkplain #decodeNull(Object) decoded} before being used.
      */
     public UnmodifiableEconomicMap<OptionKey<?>, Object> getMap() {
         return values;
@@ -98,15 +92,25 @@
         return map;
     }
 
-    public OptionValues(EconomicMap<OptionKey<?>, Object> values) {
-        MapCursor<OptionKey<?>, Object> cursor = values.getEntries();
+    public OptionValues(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
+        EconomicMap<OptionKey<?>, Object> map = newOptionMap();
+        initMap(map, values);
+        this.values = map;
+    }
+
+    protected static void initMap(EconomicMap<OptionKey<?>, Object> map, UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
+        UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries();
         while (cursor.advance()) {
-            this.values.put(cursor.getKey(), encodeNull(cursor.getValue()));
+            map.put(cursor.getKey(), encodeNull(cursor.getValue()));
         }
     }
 
+    protected <T> T get(OptionKey<T> key) {
+        return get(values, key);
+    }
+
     @SuppressWarnings("unchecked")
-    <T> T get(OptionKey<T> key) {
+    protected static <T> T get(UnmodifiableEconomicMap<OptionKey<?>, Object> values, OptionKey<T> key) {
         Object value = values.get(key);
         if (value == null) {
             return key.getDefaultValue();
@@ -116,16 +120,23 @@
 
     private static final Object NULL = new Object();
 
-    private static Object encodeNull(Object value) {
+    protected static Object encodeNull(Object value) {
         return value == null ? NULL : value;
     }
 
-    private static Object decodeNull(Object value) {
+    /**
+     * Decodes a value that may be the sentinel value for {@code null} in a map.
+     */
+    protected static Object decodeNull(Object value) {
         return value == NULL ? null : value;
     }
 
     @Override
     public String toString() {
+        return toString(getMap());
+    }
+
+    public static String toString(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
         Comparator<OptionKey<?>> comparator = new Comparator<OptionKey<?>>() {
             @Override
             public int compare(OptionKey<?> o1, OptionKey<?> o2) {
@@ -133,9 +144,9 @@
             }
         };
         SortedMap<OptionKey<?>, Object> sorted = new TreeMap<>(comparator);
-        MapCursor<OptionKey<?>, Object> cursor = values.getEntries();
+        UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries();
         while (cursor.advance()) {
-            sorted.put(cursor.getKey(), cursor.getValue());
+            sorted.put(cursor.getKey(), decodeNull(cursor.getValue()));
         }
         return sorted.toString();
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java	Thu Apr 06 14:31:32 2017 -0700
@@ -23,6 +23,7 @@
 package org.graalvm.compiler.options;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Formatter;
 import java.util.List;
 import java.util.ServiceLoader;
@@ -225,13 +226,29 @@
     private static List<OptionDescriptor> fuzzyMatch(Iterable<OptionDescriptors> loader, String optionName) {
         List<OptionDescriptor> matches = new ArrayList<>();
         for (OptionDescriptors options : loader) {
-            for (OptionDescriptor option : options) {
-                float score = stringSimiliarity(option.getName(), optionName);
-                if (score >= FUZZY_MATCH_THRESHOLD) {
-                    matches.add(option);
-                }
-            }
+            collectFuzzyMatches(options, optionName, matches);
         }
         return matches;
     }
+
+    /**
+     * Collects the set of options that fuzzy match a given option name. String similarity for fuzzy
+     * matching is based on Dice's coefficient.
+     *
+     * @param toSearch the set of option descriptors to search
+     * @param name the option name to search for
+     * @param matches the collection to which fuzzy matches of {@code name} will be added
+     * @return whether any fuzzy matches were found
+     */
+    public static boolean collectFuzzyMatches(Iterable<OptionDescriptor> toSearch, String name, Collection<OptionDescriptor> matches) {
+        boolean found = false;
+        for (OptionDescriptor option : toSearch) {
+            float score = stringSimiliarity(option.getName(), name);
+            if (score >= FUZZY_MATCH_THRESHOLD) {
+                found = true;
+                matches.add(option);
+            }
+        }
+        return found;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/UniquePathUtilities.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/UniquePathUtilities.java	Thu Apr 06 14:31:32 2017 -0700
@@ -113,6 +113,6 @@
         if (result.isAbsolute() || defaultDirectory == null) {
             return result;
         }
-        return Paths.get(defaultDirectory.getValue(options), name);
+        return Paths.get(defaultDirectory.getValue(options), name).normalize();
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AbstractInliningPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AbstractInliningPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -29,4 +29,8 @@
  * Common superclass for phases that perform inlining.
  */
 public abstract class AbstractInliningPhase extends BasePhase<HighTierContext> {
+    @Override
+    protected boolean isInliningPhase() {
+        return true;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -218,13 +218,13 @@
                 public void usagesDroppedToZero(Node node) {
                     workList.add(node);
                 }
+            };
 
-            };
             try (NodeEventScope nes = graph.trackNodeEvents(listener)) {
                 for (Node n : workList) {
                     boolean changed = processNode(n);
-                    if (changed && Debug.isDumpEnabled(Debug.DETAILED_LOG_LEVEL)) {
-                        Debug.dump(Debug.DETAILED_LOG_LEVEL, graph, "CanonicalizerPhase %s", n);
+                    if (changed && Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
+                        Debug.dump(Debug.DETAILED_LEVEL, graph, "CanonicalizerPhase %s", n);
                     }
                 }
             }
@@ -329,7 +329,7 @@
                 }
 
                 if (nodeClass.isSimplifiable() && simplify) {
-                    Debug.log(Debug.VERBOSE_LOG_LEVEL, "Canonicalizer: simplifying %s", node);
+                    Debug.log(Debug.VERBOSE_LEVEL, "Canonicalizer: simplifying %s", node);
                     COUNTER_SIMPLIFICATION_CONSIDERED_NODES.increment();
                     node.simplify(tool);
                     return node.isDeleted();
@@ -356,7 +356,7 @@
 // @formatter:on
         private boolean performReplacement(final Node node, Node newCanonical) {
             if (newCanonical == node) {
-                Debug.log(Debug.VERBOSE_LOG_LEVEL, "Canonicalizer: work on %1s", node);
+                Debug.log(Debug.VERBOSE_LEVEL, "Canonicalizer: work on %1s", node);
                 return false;
             } else {
                 Node canonical = newCanonical;
@@ -448,7 +448,7 @@
             public void deleteBranch(Node branch) {
                 FixedNode fixedBranch = (FixedNode) branch;
                 fixedBranch.predecessor().replaceFirstSuccessor(fixedBranch, null);
-                GraphUtil.killCFG(fixedBranch, this);
+                GraphUtil.killCFG(fixedBranch);
             }
 
             @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DominatorConditionalEliminationPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DominatorConditionalEliminationPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -908,6 +908,10 @@
 
         @Override
         public Node apply(Node node, Node curNode) {
+            if (!ok) {
+                // Abort the recursion
+                return curNode;
+            }
             if (!(curNode instanceof ValueNode)) {
                 ok = false;
                 return curNode;
@@ -990,7 +994,7 @@
         }
 
         public void pushElement(InfoElement element) {
-            Debug.log(Debug.VERBOSE_LOG_LEVEL, "Pushing an info element:%s", element);
+            Debug.log(Debug.VERBOSE_LEVEL, "Pushing an info element:%s", element);
             infos.add(element);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LockEliminationPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LockEliminationPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -28,6 +28,7 @@
 import org.graalvm.compiler.nodes.java.AccessMonitorNode;
 import org.graalvm.compiler.nodes.java.MonitorEnterNode;
 import org.graalvm.compiler.nodes.java.MonitorExitNode;
+import org.graalvm.compiler.nodes.java.MonitorIdNode;
 import org.graalvm.compiler.nodes.java.RawMonitorEnterNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.Phase;
@@ -36,16 +37,25 @@
 
     @Override
     protected void run(StructuredGraph graph) {
-        for (MonitorExitNode node : graph.getNodes(MonitorExitNode.TYPE)) {
-            FixedNode next = node.next();
+        for (MonitorExitNode monitorExitNode : graph.getNodes(MonitorExitNode.TYPE)) {
+            FixedNode next = monitorExitNode.next();
             if ((next instanceof MonitorEnterNode || next instanceof RawMonitorEnterNode)) {
                 // should never happen, osr monitor enters are always direct successors of the graph
                 // start
                 assert !(next instanceof OSRMonitorEnterNode);
                 AccessMonitorNode monitorEnterNode = (AccessMonitorNode) next;
-                if (GraphUtil.unproxify(monitorEnterNode.object()) == GraphUtil.unproxify(node.object())) {
+                if (GraphUtil.unproxify(monitorEnterNode.object()) == GraphUtil.unproxify(monitorExitNode.object())) {
+                    /*
+                     * We've coarsened the lock so use the same monitor id for the whole region,
+                     * otherwise the monitor operations appear to be unrelated.
+                     */
+                    MonitorIdNode enterId = monitorEnterNode.getMonitorId();
+                    MonitorIdNode exitId = monitorExitNode.getMonitorId();
+                    if (enterId != exitId) {
+                        enterId.replaceAndDelete(exitId);
+                    }
                     GraphUtil.removeFixedWithUnusedInputs(monitorEnterNode);
-                    GraphUtil.removeFixedWithUnusedInputs(node);
+                    GraphUtil.removeFixedWithUnusedInputs(monitorExitNode);
                 }
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NewConditionalEliminationPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NewConditionalEliminationPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -135,8 +135,8 @@
             }
 
             AbstractBeginNode beginNode = b.getBeginNode();
-            if (beginNode instanceof MergeNode && anchorBlock != b) {
-                MergeNode mergeNode = (MergeNode) beginNode;
+            if (beginNode instanceof AbstractMergeNode && anchorBlock != b) {
+                AbstractMergeNode mergeNode = (AbstractMergeNode) beginNode;
                 for (GuardNode guard : mergeNode.guards().snapshot()) {
                     try (DebugCloseable closeable = guard.withNodeSourcePosition()) {
                         GuardNode newlyCreatedGuard = new GuardNode(guard.getCondition(), anchorBlock.getBeginNode(), guard.getReason(), guard.getAction(), guard.isNegated(), guard.getSpeculation());
@@ -411,7 +411,7 @@
                                 allow = true;
                             } else if (integerStamp.asConstant() != null) {
                                 allow = true;
-                            } else if (oldStamp.unrestricted().equals(oldStamp)) {
+                            } else if (oldStamp.isUnrestricted()) {
                                 allow = true;
                             }
                         } else {
@@ -432,8 +432,8 @@
                                     if (input == null) {
                                         input = valueAt;
                                     }
-                                    PiNode piNode = graph.unique(new PiNode(input, curBestStamp, (ValueNode) infoElement.guard));
-                                    valueAt = piNode;
+                                    ValueNode valueNode = graph.maybeAddOrUnique(PiNode.create(input, curBestStamp, (ValueNode) infoElement.guard));
+                                    valueAt = valueNode;
                                 }
                                 newPhi.addInput(valueAt);
                             }
@@ -948,6 +948,10 @@
 
         @Override
         public Node apply(Node node, Node curNode) {
+            if (!ok) {
+                // Abort the recursion
+                return curNode;
+            }
             if (!(curNode instanceof ValueNode)) {
                 ok = false;
                 return curNode;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java	Thu Apr 06 14:31:32 2017 -0700
@@ -402,7 +402,7 @@
                 EconomicSet<Node> parameterUsages = calleeInfo.inline(new Providers(context));
                 canonicalizedNodes.addAll(parameterUsages);
                 counterInliningRuns.increment();
-                Debug.dump(Debug.INFO_LOG_LEVEL, callerGraph, "after %s", calleeInfo);
+                Debug.dump(Debug.DETAILED_LEVEL, callerGraph, "after %s", calleeInfo);
 
                 Graph.Mark markBeforeCanonicalization = callerGraph.getMark();
 
@@ -572,7 +572,7 @@
      * Gets the call hierarchy of this inlining from outer most call to inner most callee.
      */
     private Object[] inliningContext() {
-        if (!Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+        if (!Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
             return NO_CONTEXT;
         }
         Object[] result = new Object[graphQueue.size()];
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -135,6 +135,55 @@
         apply(graph, context, true);
     }
 
+    private BasePhase<?> getEnclosingPhase() {
+        for (Object c : Debug.context()) {
+            if (c != this && c instanceof BasePhase) {
+                if (!(c instanceof PhaseSuite)) {
+                    return (BasePhase<?>) c;
+                }
+            }
+        }
+        return null;
+    }
+
+    private BasePhase<?> dumpBefore(StructuredGraph graph) {
+        BasePhase<?> enclosingPhase = getEnclosingPhase();
+        boolean isTopLevel = enclosingPhase == null;
+        if (isTopLevel) {
+            if (Debug.isDumpEnabled(Debug.VERBOSE_LEVEL)) {
+                Debug.dump(Debug.VERBOSE_LEVEL, graph, "Before phase %s", getName());
+            }
+        } else {
+            if (Debug.isDumpEnabled(Debug.VERBOSE_LEVEL + 1)) {
+                Debug.dump(Debug.VERBOSE_LEVEL + 1, graph, "Before phase %s", getName());
+            }
+        }
+        return enclosingPhase;
+    }
+
+    protected boolean isInliningPhase() {
+        return false;
+    }
+
+    private void dumpAfter(BasePhase<?> enclosingPhase, StructuredGraph graph) {
+        boolean isTopLevel = enclosingPhase == null;
+        if (isTopLevel) {
+            if (isInliningPhase()) {
+                if (Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
+                    Debug.dump(Debug.BASIC_LEVEL, graph, "After phase %s", getName());
+                }
+            } else {
+                if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
+                    Debug.dump(Debug.INFO_LEVEL, graph, "After phase %s", getName());
+                }
+            }
+        } else {
+            if (Debug.isDumpEnabled(Debug.INFO_LEVEL + 1)) {
+                Debug.dump(Debug.INFO_LEVEL + 1, graph, "After phase %s", getName());
+            }
+        }
+    }
+
     @SuppressWarnings("try")
     protected final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) {
         graph.checkCancellation();
@@ -149,8 +198,9 @@
                     before = graph.getMark();
                 }
             }
-            if (dumpGraph && Debug.isDumpEnabled(Debug.VERBOSE_LOG_LEVEL)) {
-                Debug.dump(Debug.VERBOSE_LOG_LEVEL, graph, "Before phase %s", getName());
+            BasePhase<?> enclosingPhase = null;
+            if (dumpGraph && Debug.isEnabled()) {
+                enclosingPhase = dumpBefore(graph);
             }
             inputNodesCount.add(graph.getNodeCount());
             this.run(graph, context);
@@ -163,8 +213,9 @@
                     }
                 }
             }
-            if (dumpGraph && Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
-                Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "%s", getName());
+
+            if (dumpGraph && Debug.isEnabled()) {
+                dumpAfter(enclosingPhase, graph);
             }
             if (Fingerprint.ENABLED) {
                 String graphDesc = graph.method() == null ? graph.name : graph.method().format("%H.%n(%p)");
@@ -204,5 +255,4 @@
     public float codeSizeIncrease() {
         return 1.25f;
     }
-
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/MergeableState.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/MergeableState.java	Thu Apr 06 14:31:32 2017 -0700
@@ -40,7 +40,7 @@
      * contains the states of the of the other branches in the order of the merge's end nodes.
      *
      * @param merge the merge node
-     * @param withStates the state at the the merge's end node except the first one.
+     * @param withStates the state at the merge's end node except the first one.
      */
     public abstract boolean merge(AbstractMergeNode merge, List<T> withStates);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java	Thu Apr 06 14:31:32 2017 -0700
@@ -22,9 +22,19 @@
  */
 package org.graalvm.compiler.phases.verify;
 
+import static org.graalvm.compiler.debug.Debug.BASIC_LEVEL;
+import static org.graalvm.compiler.debug.Debug.DETAILED_LEVEL;
+import static org.graalvm.compiler.debug.Debug.INFO_LEVEL;
+import static org.graalvm.compiler.debug.Debug.VERBOSE_LEVEL;
+import static org.graalvm.compiler.debug.Debug.VERY_DETAILED_LEVEL;
+
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.DebugMethodMetrics;
 import org.graalvm.compiler.debug.GraalError;
@@ -33,12 +43,16 @@
 import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
 import org.graalvm.compiler.nodes.java.NewArrayNode;
 import org.graalvm.compiler.nodes.java.StoreIndexedNode;
 import org.graalvm.compiler.phases.VerifyPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 
+import jdk.vm.ci.meta.Constant;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.PrimitiveConstant;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
@@ -61,20 +75,24 @@
         return false;
     }
 
+    MetaAccessProvider metaAccess;
+
     @Override
     protected boolean verify(StructuredGraph graph, PhaseContext context) {
-        ResolvedJavaType debugType = context.getMetaAccess().lookupJavaType(Debug.class);
-        ResolvedJavaType nodeType = context.getMetaAccess().lookupJavaType(Node.class);
-        ResolvedJavaType stringType = context.getMetaAccess().lookupJavaType(String.class);
-        ResolvedJavaType debugMethodMetricsType = context.getMetaAccess().lookupJavaType(DebugMethodMetrics.class);
-        ResolvedJavaType graalErrorType = context.getMetaAccess().lookupJavaType(GraalError.class);
+        metaAccess = context.getMetaAccess();
+        ResolvedJavaType debugType = metaAccess.lookupJavaType(Debug.class);
+        ResolvedJavaType nodeType = metaAccess.lookupJavaType(Node.class);
+        ResolvedJavaType stringType = metaAccess.lookupJavaType(String.class);
+        ResolvedJavaType debugMethodMetricsType = metaAccess.lookupJavaType(DebugMethodMetrics.class);
+        ResolvedJavaType graalErrorType = metaAccess.lookupJavaType(GraalError.class);
 
         for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) {
             ResolvedJavaMethod callee = t.targetMethod();
             String calleeName = callee.getName();
             if (callee.getDeclaringClass().equals(debugType)) {
-                if (calleeName.equals("log") || calleeName.equals("logAndIndent") || calleeName.equals("verify") || calleeName.equals("dump")) {
-                    verifyParameters(t, graph, t.arguments(), stringType, calleeName.equals("dump") ? 2 : 1);
+                boolean isDump = calleeName.equals("dump");
+                if (calleeName.equals("log") || calleeName.equals("logAndIndent") || calleeName.equals("verify") || isDump) {
+                    verifyParameters(t, graph, t.arguments(), stringType, isDump ? 2 : 1);
                 }
             }
             if (callee.getDeclaringClass().isAssignableFrom(nodeType)) {
@@ -99,10 +117,10 @@
         return true;
     }
 
-    private static void verifyParameters(MethodCallTargetNode callTarget, StructuredGraph callerGraph, NodeInputList<? extends Node> args, ResolvedJavaType stringType, int startArgIdx) {
+    private void verifyParameters(MethodCallTargetNode callTarget, StructuredGraph callerGraph, NodeInputList<? extends ValueNode> args, ResolvedJavaType stringType, int startArgIdx) {
         if (callTarget.targetMethod().isVarArgs() && args.get(args.count() - 1) instanceof NewArrayNode) {
             // unpack the arguments to the var args
-            List<Node> unpacked = new ArrayList<>(args.snapshot());
+            List<ValueNode> unpacked = new ArrayList<>(args.snapshot());
             NewArrayNode varArgParameter = (NewArrayNode) unpacked.remove(unpacked.size() - 1);
             int firstVarArg = unpacked.size();
             for (Node usage : varArgParameter.usages()) {
@@ -111,18 +129,57 @@
                     unpacked.add(si.value());
                 }
             }
-            verifyParameters(callerGraph, callTarget.targetMethod(), unpacked, stringType, startArgIdx, firstVarArg);
+            verifyParameters(callerGraph, callTarget, unpacked, stringType, startArgIdx, firstVarArg);
         } else {
-            verifyParameters(callerGraph, callTarget.targetMethod(), args, stringType, startArgIdx, -1);
+            verifyParameters(callerGraph, callTarget, args, stringType, startArgIdx, -1);
         }
     }
 
-    private static void verifyParameters(StructuredGraph callerGraph, ResolvedJavaMethod verifiedCallee, List<? extends Node> args, ResolvedJavaType stringType, int startArgIdx, int varArgsIndex) {
+    private static final Set<Integer> DebugLevels = new HashSet<>(Arrays.asList(BASIC_LEVEL, INFO_LEVEL, VERBOSE_LEVEL, DETAILED_LEVEL, VERY_DETAILED_LEVEL));
+
+    /**
+     * The set of methods allowed to call a {@code Debug.dump(...)} method with the {@code level}
+     * parameter bound to {@link Debug#BASIC_LEVEL} and the {@code object} parameter bound to a
+     * {@link StructuredGraph} value.
+     *
+     * This whitelist exists to ensure any increase in graph dumps is in line with the policy
+     * outlined by {@link Debug#BASIC_LEVEL}. If you add a *justified* graph dump at this level,
+     * then update the whitelist.
+     */
+    private static final Set<String> BasicLevelStructuredGraphDumpWhitelist = new HashSet<>(Arrays.asList(
+                    "org.graalvm.compiler.phases.BasePhase.dumpAfter",
+                    "org.graalvm.compiler.core.GraalCompiler.emitFrontEnd",
+                    "org.graalvm.compiler.truffle.PartialEvaluator.fastPartialEvaluation",
+                    "org.graalvm.compiler.truffle.PartialEvaluator.reportPerformanceWarnings",
+                    "org.graalvm.compiler.truffle.TruffleCompiler.compileMethodHelper",
+                    "org.graalvm.compiler.core.test.VerifyDebugUsageTest$ValidDumpUsagePhase.run",
+                    "org.graalvm.compiler.core.test.VerifyDebugUsageTest$InvalidConcatDumpUsagePhase.run",
+                    "org.graalvm.compiler.core.test.VerifyDebugUsageTest$InvalidDumpUsagePhase.run"));
+
+    /**
+     * The set of methods allowed to call a {@code Debug.dump(...)} method with the {@code level}
+     * parameter bound to {@link Debug#INFO_LEVEL} and the {@code object} parameter bound to a
+     * {@link StructuredGraph} value.
+     *
+     * This whitelist exists to ensure any increase in graph dumps is in line with the policy
+     * outlined by {@link Debug#INFO_LEVEL}. If you add a *justified* graph dump at this level, then
+     * update the whitelist.
+     */
+    private static final Set<String> InfoLevelStructuredGraphDumpWhitelist = new HashSet<>(Arrays.asList(
+                    "org.graalvm.compiler.core.GraalCompiler.emitFrontEnd",
+                    "org.graalvm.compiler.phases.BasePhase.dumpAfter",
+                    "org.graalvm.compiler.replacements.ReplacementsImpl$GraphMaker.makeGraph",
+                    "org.graalvm.compiler.replacements.SnippetTemplate.instantiate"));
+
+    private void verifyParameters(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, List<? extends ValueNode> args, ResolvedJavaType stringType, int startArgIdx,
+                    int varArgsIndex) {
+        ResolvedJavaMethod verifiedCallee = debugCallTarget.targetMethod();
+        Integer dumpLevel = null;
         int argIdx = startArgIdx;
         int varArgsElementIndex = 0;
         boolean reportVarArgs = false;
         for (int i = 0; i < args.size(); i++) {
-            Node arg = args.get(i);
+            ValueNode arg = args.get(i);
             if (arg instanceof Invoke) {
                 reportVarArgs = varArgsIndex >= 0 && argIdx >= varArgsIndex;
                 Invoke invoke = (Invoke) arg;
@@ -142,6 +199,15 @@
                     }
                 }
             }
+            if (i == 0) {
+                if (verifiedCallee.getName().equals("dump")) {
+                    dumpLevel = verifyDumpLevelParameter(callerGraph, debugCallTarget, verifiedCallee, arg);
+                }
+            } else if (i == 1) {
+                if (dumpLevel != null) {
+                    verifyDumpObjectParameter(callerGraph, debugCallTarget, args, verifiedCallee, dumpLevel);
+                }
+            }
             if (varArgsIndex >= 0 && i >= varArgsIndex) {
                 varArgsElementIndex++;
             }
@@ -150,6 +216,63 @@
     }
 
     /**
+     * The {@code level} arg for the {@code Debug.dump(...)} methods must be a reference to one of
+     * the {@code Debug.*_LEVEL} constants.
+     */
+    protected Integer verifyDumpLevelParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, ResolvedJavaMethod verifiedCallee, ValueNode arg)
+                    throws org.graalvm.compiler.phases.VerifyPhase.VerificationError {
+        // The 'level' arg for the Debug.dump(...) methods must be a reference to one of
+        // the Debug.*_LEVEL constants.
+
+        Constant c = arg.asConstant();
+        if (c != null) {
+            Integer dumpLevel = ((PrimitiveConstant) c).asInt();
+            if (!DebugLevels.contains(dumpLevel)) {
+                StackTraceElement e = callerGraph.method().asStackTraceElement(debugCallTarget.invoke().bci());
+                throw new VerificationError(
+                                "In %s: parameter 0 of call to %s does not match a Debug.*_LEVEL constant.%n", e, verifiedCallee.format("%H.%n(%p)"));
+            }
+            return dumpLevel;
+        }
+        StackTraceElement e = callerGraph.method().asStackTraceElement(debugCallTarget.invoke().bci());
+        throw new VerificationError(
+                        "In %s: parameter 0 of call to %s must be a constant.%n", e, verifiedCallee.format("%H.%n(%p)"));
+    }
+
+    protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, List<? extends ValueNode> args, ResolvedJavaMethod verifiedCallee, Integer dumpLevel)
+                    throws org.graalvm.compiler.phases.VerifyPhase.VerificationError {
+        ResolvedJavaType arg1Type = ((ObjectStamp) args.get(1).stamp()).type();
+        if (metaAccess.lookupJavaType(StructuredGraph.class).isAssignableFrom(arg1Type)) {
+            verifyStructuredGraphDumping(callerGraph, debugCallTarget, verifiedCallee, dumpLevel);
+        }
+    }
+
+    /**
+     * Verifies that dumping a {@link StructuredGraph} at level {@link Debug#BASIC_LEVEL} or
+     * {@link Debug#INFO_LEVEL} only occurs in white-listed methods.
+     */
+    protected void verifyStructuredGraphDumping(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, ResolvedJavaMethod verifiedCallee, Integer dumpLevel)
+                    throws org.graalvm.compiler.phases.VerifyPhase.VerificationError {
+        if (dumpLevel == Debug.BASIC_LEVEL) {
+            StackTraceElement e = callerGraph.method().asStackTraceElement(debugCallTarget.invoke().bci());
+            String qualifiedMethod = e.getClassName() + "." + e.getMethodName();
+            if (!BasicLevelStructuredGraphDumpWhitelist.contains(qualifiedMethod)) {
+                throw new VerificationError(
+                                "In %s: call to %s with level == Debug.BASIC_LEVEL not in %s.BasicLevelDumpWhitelist.%n", e, verifiedCallee.format("%H.%n(%p)"),
+                                getClass().getName());
+            }
+        } else if (dumpLevel == Debug.INFO_LEVEL) {
+            StackTraceElement e = callerGraph.method().asStackTraceElement(debugCallTarget.invoke().bci());
+            String qualifiedMethod = e.getClassName() + "." + e.getMethodName();
+            if (!InfoLevelStructuredGraphDumpWhitelist.contains(qualifiedMethod)) {
+                throw new VerificationError(
+                                "In %s: call to %s with level == Debug.INFO_LEVEL not in %s.InfoLevelDumpWhitelist.%n", e, verifiedCallee.format("%H.%n(%p)"),
+                                getClass().getName());
+            }
+        }
+    }
+
+    /**
      * Checks that a given call is not to {@link StringBuffer#toString()} or
      * {@link StringBuilder#toString()}.
      */
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java	Thu Apr 06 14:31:32 2017 -0700
@@ -158,7 +158,7 @@
         } else if (isToStringTrusted(c)) {
             return value.toString();
         }
-        return MetaUtil.getSimpleName(c, true) + "@" + System.identityHashCode(value);
+        return MetaUtil.getSimpleName(c, true) + "@" + Integer.toHexString(System.identityHashCode(value));
 
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java	Thu Apr 06 14:31:32 2017 -0700
@@ -173,6 +173,9 @@
                 Map<Object, Object> properties = new HashMap<>();
                 properties.put("graph", graph.toString());
                 properties.put("scope", Debug.currentScope());
+                if (graph instanceof StructuredGraph) {
+                    properties.put("compilationIdentifier", ((StructuredGraph) graph).compilationId());
+                }
                 addCFGFileName(properties);
                 printer.print(graph, nextDumpId() + ":" + message, properties);
             } catch (IOException e) {
@@ -298,7 +301,6 @@
     }
 
     private void openScope(String name, int inlineDepth, Map<Object, Object> properties) {
-        String prefix = inlineDepth == 0 ? Thread.currentThread().getName() + ":" : "";
         try {
             Map<Object, Object> props = properties;
             if (inlineDepth == 0) {
@@ -312,7 +314,7 @@
                 }
                 props.put("date", new Date().toString());
             }
-            printer.beginGroup(prefix + name, name, Debug.contextLookup(ResolvedJavaMethod.class), -1, props);
+            printer.beginGroup(name, name, Debug.contextLookup(ResolvedJavaMethod.class), -1, props);
         } catch (IOException e) {
             handleException(e);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/InstanceOfTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/InstanceOfTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -488,7 +488,7 @@
         try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             compile(graph.method(), graph);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
+            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -56,9 +56,9 @@
         try (Scope s = Debug.scope("MethodSubstitutionTest", getResolvedJavaMethod(snippet))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             HighTierContext context = getDefaultHighTierContext();
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
             new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
+            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
             new CanonicalizerPhase().apply(graph, context);
             new DeadCodeEliminationPhase().apply(graph);
             // Try to ensure any macro nodes are lowered to expose any resulting invokes
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -133,11 +133,12 @@
         try (Debug.Scope scope = Debug.scope("GraphPETest", testMethod)) {
             GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withEagerResolving(true);
             registerPlugins(graphBuilderConfig.getPlugins().getInvocationPlugins());
-            CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(getProviders(), graphBuilderConfig, OptimisticOptimizations.NONE, AllowAssumptions.YES, getTarget().arch, getInitialOptions());
+            targetGraph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).method(testMethod).build();
+            CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(getTarget().arch, targetGraph, getProviders(), graphBuilderConfig, OptimisticOptimizations.NONE, AllowAssumptions.YES,
+                            getInitialOptions(), null, null, new InlineInvokePlugin[]{new InlineAll()}, null);
 
-            targetGraph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).method(testMethod).build();
-            decoder.decode(targetGraph, testMethod, null, null, new InlineInvokePlugin[]{new InlineAll()}, null);
-            Debug.dump(Debug.BASIC_LOG_LEVEL, targetGraph, "Target Graph");
+            decoder.decode(testMethod);
+            Debug.dump(Debug.BASIC_LEVEL, targetGraph, "Target Graph");
             targetGraph.verify();
 
             PhaseContext context = new PhaseContext(getProviders());
@@ -146,7 +147,7 @@
 
         } catch (Throwable ex) {
             if (targetGraph != null) {
-                Debug.dump(Debug.BASIC_LOG_LEVEL, targetGraph, ex.toString());
+                Debug.dump(Debug.BASIC_LEVEL, targetGraph, ex.toString());
             }
             Debug.handle(ex);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionsTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionsTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -133,7 +133,7 @@
     @Override
     protected Plugins getDefaultGraphBuilderPlugins() {
         Plugins ret = super.getDefaultGraphBuilderPlugins();
-        // manually register generated factories, jvmci service providers don't work from unit tests
+        // manually register generated factories, Graal service providers don't work from unit tests
         new PluginFactory_SubstitutionsTest().registerPlugins(ret.getInvocationPlugins(), null);
         return ret;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/GeneratedNodeIntrinsicPlugin.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/GeneratedNodeIntrinsicPlugin.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,12 +32,10 @@
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
-import javax.tools.Diagnostic.Kind;
 
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.InjectedNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
-import org.graalvm.compiler.replacements.verifier.InjectedDependencies.WellKnownDependency;
 
 /**
  * Create graph builder plugins for {@link NodeIntrinsic} methods.
@@ -124,10 +122,6 @@
             }
             out.printf(");\n");
 
-            if (intrinsicMethod.getAnnotation(NodeIntrinsic.class).setStampFromReturnType()) {
-                out.printf("            node.setStamp(%s);\n", deps.use(WellKnownDependency.RETURN_STAMP));
-            }
-
             if (intrinsicMethod.getReturnType().getKind() == TypeKind.VOID) {
                 out.printf("            b.add(node);\n");
             } else {
@@ -164,10 +158,6 @@
                 out.printf(", arg%d", i);
             }
             out.printf(");\n");
-
-            if (intrinsicMethod.getAnnotation(NodeIntrinsic.class).setStampFromReturnType()) {
-                env.getMessager().printMessage(Kind.WARNING, "Ignoring setStampFromReturnType because a custom 'intrinsify' method is used.", intrinsicMethod);
-            }
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/InjectedDependencies.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/InjectedDependencies.java	Thu Apr 06 14:31:32 2017 -0700
@@ -60,23 +60,24 @@
         }
     }
 
-    private static final class StampDependency extends Dependency {
+    private static final class InjectedStampDependency extends Dependency {
 
-        private StampDependency() {
-            super("returnStamp", "org.graalvm.compiler.core.common.type.Stamp");
+        private InjectedStampDependency() {
+            super("stamp", "org.graalvm.compiler.core.common.type.Stamp");
         }
 
         @Override
         public String inject(ExecutableElement inject) {
             NodeIntrinsic nodeIntrinsic = inject.getAnnotation(NodeIntrinsic.class);
-            return String.format("injection.getReturnStamp(%s.class, %s)", GeneratedPlugin.getErasedType(inject.getReturnType()), nodeIntrinsic != null && nodeIntrinsic.returnStampIsNonNull());
+            boolean nonNull = nodeIntrinsic != null && nodeIntrinsic.injectedStampIsNonNull();
+            return String.format("injection.getInjectedStamp(%s.class, %s)", GeneratedPlugin.getErasedType(inject.getReturnType()), nonNull);
         }
     }
 
     public enum WellKnownDependency {
         CONSTANT_REFLECTION("b.getConstantReflection()", "jdk.vm.ci.meta.ConstantReflectionProvider"),
         META_ACCESS("b.getMetaAccess()", "jdk.vm.ci.meta.MetaAccessProvider"),
-        RETURN_STAMP(new StampDependency()),
+        INJECTED_STAMP(new InjectedStampDependency()),
         SNIPPET_REFLECTION(new InjectedDependency("snippetReflection", "org.graalvm.compiler.api.replacements.SnippetReflectionProvider")),
         STAMP_PROVIDER("b.getStampProvider()", "org.graalvm.compiler.nodes.spi.StampProvider"),
         STRUCTURED_GRAPH("b.getGraph()", "org.graalvm.compiler.nodes.StructuredGraph");
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/NodeIntrinsicVerifier.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/NodeIntrinsicVerifier.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,7 +25,11 @@
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.AnnotationMirror;
@@ -57,6 +61,10 @@
         return env.getElementUtils().getTypeElement("org.graalvm.compiler.graph.Node").asType();
     }
 
+    private TypeMirror stampType() {
+        return env.getElementUtils().getTypeElement("org.graalvm.compiler.core.common.type.Stamp").asType();
+    }
+
     private TypeMirror valueNodeType() {
         return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodes.ValueNode").asType();
     }
@@ -118,32 +126,68 @@
             }
         }
 
-        if (intrinsicMethod.getReturnType() instanceof TypeVariable) {
+        TypeMirror returnType = intrinsicMethod.getReturnType();
+        if (returnType instanceof TypeVariable) {
             env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic cannot have a generic return type.", element, annotation);
         }
 
+        boolean injectedStampIsNonNull = intrinsicMethod.getAnnotation(NodeIntrinsic.class).injectedStampIsNonNull();
+
+        if (returnType.getKind() == TypeKind.VOID) {
+            for (VariableElement parameter : intrinsicMethod.getParameters()) {
+                if (parameter.getAnnotation(InjectedNodeParameter.class) != null) {
+                    env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic with an injected Stamp parameter cannot have a void return type.", element, annotation);
+                    break;
+                }
+            }
+        }
+
         TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod);
-        ExecutableElement custom = findCustomIntrinsifyMethod(nodeClass, constructorSignature);
-        if (custom != null) {
-            generator.addPlugin(new GeneratedNodeIntrinsicPlugin.CustomFactoryPlugin(intrinsicMethod, custom, constructorSignature));
+        Map<ExecutableElement, String> nonMatches = new HashMap<>();
+        List<ExecutableElement> factories = findIntrinsifyFactoryMethod(nodeClass, constructorSignature, nonMatches, injectedStampIsNonNull);
+        List<ExecutableElement> constructors = Collections.emptyList();
+        if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) {
+            if (factories.isEmpty()) {
+                env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make a node intrinsic for an abstract class %s.", nodeClass.getSimpleName()), element, annotation);
+            }
+        } else if (!isNodeType(nodeClass)) {
+            if (factories.isEmpty()) {
+                env.getMessager().printMessage(Kind.ERROR, String.format("%s is not a subclass of %s.", nodeClass.getSimpleName(), nodeType()), element, annotation);
+            }
         } else {
-            if (isNodeType(nodeClass)) {
-                if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) {
-                    env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation);
-                } else {
-                    TypeMirror ret = intrinsicMethod.getReturnType();
-                    if (env.getTypeUtils().isAssignable(ret, structuralInputType())) {
-                        checkInputType(nodeClass, ret, element, annotation);
-                    }
+            TypeMirror ret = returnType;
+            if (env.getTypeUtils().isAssignable(ret, structuralInputType())) {
+                checkInputType(nodeClass, ret, element, annotation);
+            }
 
-                    ExecutableElement constructor = findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation);
-                    if (constructor != null) {
-                        generator.addPlugin(new GeneratedNodeIntrinsicPlugin.ConstructorPlugin(intrinsicMethod, constructor, constructorSignature));
-                    }
+            constructors = findConstructors(nodeClass, constructorSignature, nonMatches, injectedStampIsNonNull);
+        }
+        Formatter msg = new Formatter();
+        if (factories.size() > 1) {
+            msg.format("Found more than one factory in %s matching node intrinsic:", nodeClass);
+            for (ExecutableElement candidate : factories) {
+                msg.format("%n  %s", candidate);
+            }
+            env.getMessager().printMessage(Kind.ERROR, msg.toString(), intrinsicMethod, annotation);
+        } else if (constructors.size() > 1) {
+            msg.format("Found more than one constructor in %s matching node intrinsic:", nodeClass);
+            for (ExecutableElement candidate : constructors) {
+                msg.format("%n  %s", candidate);
+            }
+            env.getMessager().printMessage(Kind.ERROR, msg.toString(), intrinsicMethod, annotation);
+        } else if (factories.size() == 1) {
+            generator.addPlugin(new GeneratedNodeIntrinsicPlugin.CustomFactoryPlugin(intrinsicMethod, factories.get(0), constructorSignature));
+        } else if (constructors.size() == 1) {
+            generator.addPlugin(new GeneratedNodeIntrinsicPlugin.ConstructorPlugin(intrinsicMethod, constructors.get(0), constructorSignature));
+        } else {
+            msg.format("Could not find any factories or constructors in %s matching node intrinsic", nodeClass);
+            if (!nonMatches.isEmpty()) {
+                msg.format("%nFactories and constructors that failed to match:");
+                for (Map.Entry<ExecutableElement, String> e : nonMatches.entrySet()) {
+                    msg.format("%n  %s: %s", e.getKey(), e.getValue());
                 }
-            } else {
-                env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation);
             }
+            env.getMessager().printMessage(Kind.ERROR, msg.toString(), intrinsicMethod, annotation);
         }
     }
 
@@ -201,34 +245,20 @@
         return parameters;
     }
 
-    private ExecutableElement findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) {
+    private List<ExecutableElement> findConstructors(TypeElement nodeClass, TypeMirror[] signature, Map<ExecutableElement, String> nonMatches, boolean requiresInjectedStamp) {
         List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements());
-        List<String> failureReasons = new ArrayList<>();
-
+        List<ExecutableElement> found = new ArrayList<>(constructors.size());
         for (ExecutableElement constructor : constructors) {
-            String failureReason = matchSignature(0, constructor, signature);
-            if (failureReason == null) {
-                // found
-                return constructor;
-            }
-
-            failureReasons.add(failureReason);
-        }
-
-        // not found
-        if (failureReasons.isEmpty()) {
-            env.getMessager().printMessage(Kind.ERROR, "Could not find matching constructor for node intrinsic.", intrinsicMethod, intrinsicAnnotation);
-        } else {
-            for (String reason : failureReasons) {
-                env.getMessager().printMessage(Kind.ERROR, reason, intrinsicMethod, intrinsicAnnotation);
+            if (matchSignature(0, constructor, signature, nonMatches, requiresInjectedStamp)) {
+                found.add(constructor);
             }
         }
-
-        return null;
+        return found;
     }
 
-    private ExecutableElement findCustomIntrinsifyMethod(TypeElement nodeClass, TypeMirror[] signature) {
+    private List<ExecutableElement> findIntrinsifyFactoryMethod(TypeElement nodeClass, TypeMirror[] signature, Map<ExecutableElement, String> nonMatches, boolean requiresInjectedStamp) {
         List<ExecutableElement> methods = ElementFilter.methodsIn(nodeClass.getEnclosedElements());
+        List<ExecutableElement> found = new ArrayList<>(methods.size());
         for (ExecutableElement method : methods) {
             if (!method.getSimpleName().toString().equals("intrinsify")) {
                 continue;
@@ -248,50 +278,64 @@
                 continue;
             }
 
-            String failureReason = matchSignature(2, method, signature);
-            if (failureReason == null) {
-                // found
-                return method;
+            if (method.getReturnType().getKind() != TypeKind.BOOLEAN) {
+                continue;
+            }
+
+            if (matchSignature(2, method, signature, nonMatches, requiresInjectedStamp)) {
+                found.add(method);
             }
         }
-
-        return null;
+        return found;
     }
 
-    private String matchSignature(int numSkippedParameters, ExecutableElement method, TypeMirror[] signature) {
+    private boolean matchSignature(int numSkippedParameters, ExecutableElement method, TypeMirror[] signature, Map<ExecutableElement, String> nonMatches, boolean requiresInjectedStamp) {
         int sIdx = 0;
         int cIdx = numSkippedParameters;
+        boolean missingStampArgument = requiresInjectedStamp;
         while (cIdx < method.getParameters().size()) {
             VariableElement parameter = method.getParameters().get(cIdx++);
+            TypeMirror paramType = parameter.asType();
             if (parameter.getAnnotation(InjectedNodeParameter.class) != null) {
+                if (missingStampArgument && env.getTypeUtils().isSameType(paramType, stampType())) {
+                    missingStampArgument = false;
+                }
                 // skip injected parameters
                 continue;
             }
+            if (missingStampArgument) {
+                nonMatches.put(method, String.format("missing injected %s argument", stampType()));
+                return false;
+            }
 
-            TypeMirror paramType = parameter.asType();
             if (cIdx == method.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) {
                 // last argument of constructor is varargs, match remaining intrinsic arguments
                 TypeMirror varargsType = ((ArrayType) paramType).getComponentType();
                 while (sIdx < signature.length) {
                     if (!isTypeCompatible(varargsType, signature[sIdx++])) {
-                        return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, varargsType, signature[sIdx - 1]);
+                        nonMatches.put(method, String.format("the types of argument %d are incompatible: %s != %s", sIdx, varargsType, signature[sIdx - 1]));
+                        return false;
                     }
                 }
             } else if (sIdx >= signature.length) {
                 // too many arguments in intrinsic method
-                return String.format("Too many arguments for %s", method);
+                nonMatches.put(method, "too many arguments");
+                return false;
             } else if (!isTypeCompatible(paramType, signature[sIdx++])) {
-                return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, paramType, signature[sIdx - 1]);
+                nonMatches.put(method, String.format("the type of argument %d is incompatible: %s != %s", sIdx, paramType, signature[sIdx - 1]));
+                return false;
             }
         }
+        if (missingStampArgument) {
+            nonMatches.put(method, String.format("missing injected %s argument", stampType()));
+            return false;
+        }
 
-        if (sIdx == signature.length) {
-            // found
-            return null;
+        if (sIdx != signature.length) {
+            nonMatches.put(method, "not enough arguments");
+            return false;
         }
-
-        // too many arguments in constructor
-        return String.format("Not enough arguments for %s", method);
+        return true;
     }
 
     private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -32,7 +32,11 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
+import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin;
+import org.graalvm.compiler.nodes.graphbuilderconf.ParameterPlugin;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
@@ -56,9 +60,11 @@
     private final AllowAssumptions allowAssumptions;
     private final EconomicMap<ResolvedJavaMethod, EncodedGraph> graphCache;
 
-    public CachingPEGraphDecoder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, AllowAssumptions allowAssumptions,
-                    Architecture architecture, OptionValues options) {
-        super(providers.getMetaAccess(), providers.getConstantReflection(), providers.getConstantFieldProvider(), providers.getStampProvider(), architecture, options);
+    public CachingPEGraphDecoder(Architecture architecture, StructuredGraph graph, Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
+                    AllowAssumptions allowAssumptions, OptionValues options, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
+                    ParameterPlugin parameterPlugin) {
+        super(architecture, graph, providers.getMetaAccess(), providers.getConstantReflection(), providers.getConstantFieldProvider(), providers.getStampProvider(), options, loopExplosionPlugin,
+                        invocationPlugins, inlineInvokePlugins, parameterPlugin);
 
         this.providers = providers;
         this.graphBuilderConfig = graphBuilderConfig;
@@ -74,22 +80,22 @@
 
     @SuppressWarnings("try")
     private EncodedGraph createGraph(ResolvedJavaMethod method, BytecodeProvider intrinsicBytecodeProvider) {
-        StructuredGraph graph = new StructuredGraph.Builder(options, allowAssumptions).useProfilingInfo(false).method(method).build();
-        try (Debug.Scope scope = Debug.scope("createGraph", graph)) {
+        StructuredGraph graphToEncode = new StructuredGraph.Builder(options, allowAssumptions).useProfilingInfo(false).method(method).build();
+        try (Debug.Scope scope = Debug.scope("createGraph", graphToEncode)) {
             IntrinsicContext initialIntrinsicContext = intrinsicBytecodeProvider != null ? new IntrinsicContext(method, method, intrinsicBytecodeProvider, INLINE_AFTER_PARSING) : null;
             GraphBuilderPhase.Instance graphBuilderPhaseInstance = createGraphBuilderPhaseInstance(initialIntrinsicContext);
-            graphBuilderPhaseInstance.apply(graph);
+            graphBuilderPhaseInstance.apply(graphToEncode);
 
             PhaseContext context = new PhaseContext(providers);
-            new CanonicalizerPhase().apply(graph, context);
+            new CanonicalizerPhase().apply(graphToEncode, context);
             /*
              * ConvertDeoptimizeToGuardPhase reduces the number of merges in the graph, so that
              * fewer frame states will be created. This significantly reduces the number of nodes in
              * the initial graph.
              */
-            new ConvertDeoptimizeToGuardPhase().apply(graph, context);
+            new ConvertDeoptimizeToGuardPhase().apply(graphToEncode, context);
 
-            EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, architecture);
+            EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graphToEncode, architecture);
             graphCache.put(method, encodedGraph);
             return encodedGraph;
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Thu Apr 06 14:31:32 2017 -0700
@@ -44,7 +44,6 @@
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
-import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
@@ -78,11 +77,11 @@
 import org.graalvm.compiler.nodes.extended.JavaWriteNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
 import org.graalvm.compiler.nodes.extended.MembarNode;
+import org.graalvm.compiler.nodes.extended.RawLoadNode;
+import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.extended.UnboxNode;
-import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.extended.UnsafeMemoryLoadNode;
 import org.graalvm.compiler.nodes.extended.UnsafeMemoryStoreNode;
-import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
 import org.graalvm.compiler.nodes.java.AccessIndexedNode;
 import org.graalvm.compiler.nodes.java.ArrayLengthNode;
@@ -374,7 +373,7 @@
      */
     public AddressNode createArrayIndexAddress(StructuredGraph graph, ValueNode array, JavaKind elementKind, ValueNode index, GuardingNode boundsCheck) {
         IntegerStamp indexStamp = StampFactory.forInteger(32, 0, Integer.MAX_VALUE - 1);
-        ValueNode positiveIndex = graph.unique(new PiNode(index, indexStamp, boundsCheck != null ? boundsCheck.asNode() : null));
+        ValueNode positiveIndex = graph.maybeAddOrUnique(PiNode.create(index, indexStamp, boundsCheck != null ? boundsCheck.asNode() : null));
         return createArrayAddress(graph, array, elementKind, positiveIndex);
     }
 
@@ -961,7 +960,7 @@
             arrayLength = arrayLength.isAlive() ? arrayLength : graph.addOrUniqueWithInputs(arrayLength);
         }
 
-        LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength, tool.getConstantReflection());
+        LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength);
         if (boundsCheck.isTautology()) {
             return null;
         } else {
@@ -981,7 +980,7 @@
         if (nullCheck == null) {
             return object;
         } else {
-            return before.graph().unique(new PiNode(object, ((ObjectStamp) object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck));
+            return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck));
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java	Thu Apr 06 14:31:32 2017 -0700
@@ -358,7 +358,7 @@
      * {@link #endIf} to close the if-block.
      *
      * @param condition The condition for the if-block
-     * @param trueProbability The estimated probability the the condition is true
+     * @param trueProbability The estimated probability the condition is true
      */
     public void startIf(LogicNode condition, double trueProbability) {
         AbstractBeginNode thenSuccessor = graph.add(new BeginNode());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/NodeIntrinsificationProvider.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/NodeIntrinsificationProvider.java	Thu Apr 06 14:31:32 2017 -0700
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public Stamp getReturnStamp(Class<?> type, boolean nonNull) {
+    public Stamp getInjectedStamp(Class<?> type, boolean nonNull) {
         JavaKind kind = JavaKind.fromJavaClass(type);
         if (kind == JavaKind.Object) {
             ResolvedJavaType returnType = metaAccess.lookupJavaType(type);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java	Thu Apr 06 14:31:32 2017 -0700
@@ -23,14 +23,12 @@
 package org.graalvm.compiler.replacements;
 
 import static org.graalvm.compiler.debug.GraalError.unimplemented;
-import static org.graalvm.compiler.java.BytecodeParserOptions.DumpDuringGraphBuilding;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -54,6 +52,7 @@
 import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
+import org.graalvm.compiler.nodes.ControlSinkNode;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.EncodedGraph;
 import org.graalvm.compiler.nodes.FixedNode;
@@ -137,10 +136,6 @@
         protected final InvokeData invokeData;
         protected final int inliningDepth;
 
-        protected final LoopExplosionPlugin loopExplosionPlugin;
-        protected final InvocationPlugins invocationPlugins;
-        protected final InlineInvokePlugin[] inlineInvokePlugins;
-        protected final ParameterPlugin parameterPlugin;
         protected final ValueNode[] arguments;
 
         protected FrameState outerState;
@@ -149,21 +144,17 @@
         protected NodeSourcePosition callerBytecodePosition;
 
         protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData,
-                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins, ParameterPlugin parameterPlugin,
-                        ValueNode[] arguments) {
+                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, ValueNode[] arguments) {
             super(callerLoopScope, targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
 
             this.caller = caller;
             this.method = method;
             this.invokeData = invokeData;
             this.inliningDepth = inliningDepth;
-            this.loopExplosionPlugin = loopExplosionPlugin;
-            this.invocationPlugins = invocationPlugins;
-            this.inlineInvokePlugins = inlineInvokePlugins;
-            this.parameterPlugin = parameterPlugin;
             this.arguments = arguments;
         }
 
+        @Override
         public boolean isInlinedMethod() {
             return caller != null;
         }
@@ -219,7 +210,7 @@
 
         @Override
         public StructuredGraph getGraph() {
-            return methodScope.graph;
+            return graph;
         }
 
         @Override
@@ -399,10 +390,19 @@
     }
 
     protected final OptionValues options;
+    private final LoopExplosionPlugin loopExplosionPlugin;
+    private final InvocationPlugins invocationPlugins;
+    private final InlineInvokePlugin[] inlineInvokePlugins;
+    private final ParameterPlugin parameterPlugin;
 
-    public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, StampProvider stampProvider,
-                    Architecture architecture, OptionValues options) {
-        super(metaAccess, constantReflection, constantFieldProvider, stampProvider, true, architecture);
+    public PEGraphDecoder(Architecture architecture, StructuredGraph graph, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
+                    StampProvider stampProvider, OptionValues options, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
+                    ParameterPlugin parameterPlugin) {
+        super(architecture, graph, metaAccess, constantReflection, constantFieldProvider, stampProvider, true);
+        this.loopExplosionPlugin = loopExplosionPlugin;
+        this.invocationPlugins = invocationPlugins;
+        this.inlineInvokePlugins = inlineInvokePlugins;
+        this.parameterPlugin = parameterPlugin;
         this.options = options;
     }
 
@@ -414,20 +414,17 @@
         }
     }
 
-    public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
-                    ParameterPlugin parameterPlugin) {
-        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method, null), method, null, 0, loopExplosionPlugin, invocationPlugins,
-                        inlineInvokePlugins,
-                        parameterPlugin, null);
+    public void decode(ResolvedJavaMethod method) {
+        PEMethodScope methodScope = new PEMethodScope(graph, null, null, lookupEncodedGraph(method, null), method, null, 0, loopExplosionPlugin, null);
         decode(createInitialLoopScope(methodScope, null));
         cleanupGraph(methodScope);
 
-        Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "After graph cleanup");
-        assert methodScope.graph.verify();
+        Debug.dump(Debug.VERBOSE_LEVEL, graph, "After graph cleanup");
+        assert graph.verify();
 
         try {
             /* Check that the control flow graph can be computed, to catch problems early. */
-            assert CFGVerifier.verify(ControlFlowGraph.compute(methodScope.graph, true, true, true, true));
+            assert CFGVerifier.verify(ControlFlowGraph.compute(graph, true, true, true, true));
         } catch (Throwable ex) {
             throw GraalError.shouldNotReachHere("Control flow graph not valid after partial evaluation");
         }
@@ -437,7 +434,7 @@
     protected void cleanupGraph(MethodScope methodScope) {
         super.cleanupGraph(methodScope);
 
-        for (FrameState frameState : methodScope.graph.getNodes(FrameState.TYPE)) {
+        for (FrameState frameState : graph.getNodes(FrameState.TYPE)) {
             if (frameState.bci == BytecodeFrame.UNWIND_BCI) {
                 /*
                  * handleMissingAfterExceptionFrameState is called during graph decoding from
@@ -492,7 +489,7 @@
         }
 
         /* We know that we need an invoke, so now we can add the call target to the graph. */
-        methodScope.graph.add(callTarget);
+        graph.add(callTarget);
         registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false);
         return super.handleInvoke(methodScope, loopScope, invokeData);
     }
@@ -519,21 +516,21 @@
             return inlineLoopScope;
         }
 
-        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+        for (InlineInvokePlugin plugin : inlineInvokePlugins) {
             plugin.notifyNotInlined(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke);
         }
         return null;
     }
 
     protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
-        if (methodScope.invocationPlugins == null) {
+        if (invocationPlugins == null) {
             return false;
         }
 
         Invoke invoke = invokeData.invoke;
 
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
-        InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod);
+        InvocationPlugin invocationPlugin = invocationPlugins.lookupInvocation(targetMethod);
         if (invocationPlugin == null) {
             return false;
         }
@@ -546,8 +543,7 @@
          */
         invoke.asNode().replaceAtPredecessor(null);
 
-        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
-                        methodScope.invocationPlugins, methodScope.inlineInvokePlugins, null, arguments);
+        PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, loopExplosionPlugin, arguments);
         PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor);
         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
 
@@ -587,7 +583,7 @@
         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
         GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke);
 
-        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+        for (InlineInvokePlugin plugin : inlineInvokePlugins) {
             InlineInfo inlineInfo = plugin.shouldInlineInvoke(graphBuilderContext, targetMethod, arguments);
             if (inlineInfo != null) {
                 if (inlineInfo.getMethodToInline() == null) {
@@ -611,7 +607,7 @@
             throw tooDeepInlining(methodScope);
         }
 
-        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+        for (InlineInvokePlugin plugin : inlineInvokePlugins) {
             plugin.notifyBeforeInline(inlineMethod);
         }
 
@@ -620,16 +616,8 @@
         FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
         invokeNode.replaceAtPredecessor(null);
 
-        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
-                        methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugins, null, arguments);
-
-        /*
-         * After decoding all the nodes of the inlined method, we need to re-wire the return and
-         * unwind nodes. Since inlining is non-recursive, this cannot be done at the end of this
-         * method, but must be registered as a cleanup task that runs when all nodes of the inlined
-         * methods have been decoded.
-         */
-        inlineScope.cleanupTasks.add(() -> finishInlining(methodScope, loopScope, invokeData, inlineMethod, inlineScope));
+        PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
+                        loopExplosionPlugin, arguments);
 
         /*
          * Do the actual inlining by returning the initial loop scope for the inlined method scope.
@@ -637,32 +625,43 @@
         return createInitialLoopScope(inlineScope, predecessor);
     }
 
-    protected void finishInlining(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, ResolvedJavaMethod inlineMethod, PEMethodScope inlineScope) {
+    @Override
+    protected void finishInlining(MethodScope is) {
+        PEMethodScope inlineScope = (PEMethodScope) is;
+        ResolvedJavaMethod inlineMethod = inlineScope.method;
+        PEMethodScope methodScope = inlineScope.caller;
+        LoopScope loopScope = inlineScope.callerLoopScope;
+        InvokeData invokeData = inlineScope.invokeData;
         Invoke invoke = invokeData.invoke;
         FixedNode invokeNode = invoke.asNode();
 
         ValueNode exceptionValue = null;
-        List<UnwindNode> unwindNodes = inlineScope.unwindNodes;
-        Iterator<UnwindNode> iter = unwindNodes.iterator();
-        while (iter.hasNext()) {
-            if (iter.next().isDeleted()) {
-                iter.remove();
+        int returnNodeCount = 0;
+        int unwindNodeCount = 0;
+        List<ControlSinkNode> returnAndUnwindNodes = inlineScope.returnAndUnwindNodes;
+        for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
+            FixedNode fixedNode = returnAndUnwindNodes.get(i);
+            if (fixedNode instanceof ReturnNode) {
+                returnNodeCount++;
+            } else if (fixedNode.isAlive()) {
+                assert fixedNode instanceof UnwindNode;
+                unwindNodeCount++;
             }
         }
 
-        if (!unwindNodes.isEmpty()) {
+        if (unwindNodeCount > 0) {
             FixedNode unwindReplacement;
             if (invoke instanceof InvokeWithExceptionNode) {
                 /* Decoding continues for the exception handler. */
                 unwindReplacement = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
             } else {
                 /* No exception handler available, so the only thing we can do is deoptimize. */
-                unwindReplacement = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                unwindReplacement = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
             }
 
-            if (unwindNodes.size() == 1) {
+            if (unwindNodeCount == 1) {
                 /* Only one UnwindNode, we can use the exception directly. */
-                UnwindNode unwindNode = unwindNodes.get(0);
+                UnwindNode unwindNode = getSingleMatchingNode(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class);
                 exceptionValue = unwindNode.exception();
                 unwindNode.replaceAndDelete(unwindReplacement);
 
@@ -673,8 +672,9 @@
                  * also explode exception paths. Merge the exception in a similar way as multiple
                  * return values.
                  */
-                MergeNode unwindMergeNode = methodScope.graph.add(new MergeNode());
-                exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, unwindNodes, unwindNode -> unwindNode.exception());
+                MergeNode unwindMergeNode = graph.add(new MergeNode());
+                exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, getMatchingNodes(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class, unwindNodeCount),
+                                unwindNode -> unwindNode.exception());
                 unwindMergeNode.setNext(unwindReplacement);
 
                 ensureExceptionStateDecoded(inlineScope);
@@ -686,22 +686,19 @@
         assert !(invoke instanceof InvokeWithExceptionNode) || ((InvokeWithExceptionNode) invoke).exceptionEdge() == null;
 
         ValueNode returnValue;
-        List<ReturnNode> returnNodes = inlineScope.returnNodes;
-        if (!returnNodes.isEmpty()) {
-            if (returnNodes.size() == 1) {
-                ReturnNode returnNode = returnNodes.get(0);
-                returnValue = returnNode.result();
-                FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode));
-                returnNode.replaceAndDelete(n);
-            } else {
-                AbstractMergeNode merge = methodScope.graph.add(new MergeNode());
-                merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
-                returnValue = InliningUtil.mergeReturns(merge, returnNodes);
-                FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge);
-                merge.setNext(n);
-            }
+        if (returnNodeCount == 0) {
+            returnValue = null;
+        } else if (returnNodeCount == 1) {
+            ReturnNode returnNode = getSingleMatchingNode(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class);
+            returnValue = returnNode.result();
+            FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode));
+            returnNode.replaceAndDelete(n);
         } else {
-            returnValue = null;
+            AbstractMergeNode merge = graph.add(new MergeNode());
+            merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
+            returnValue = InliningUtil.mergeReturns(merge, getMatchingNodes(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class, returnNodeCount));
+            FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge);
+            merge.setNext(n);
         }
         invokeNode.replaceAtUsages(returnValue);
 
@@ -718,13 +715,42 @@
         }
         deleteInvoke(invoke);
 
-        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+        for (InlineInvokePlugin plugin : inlineInvokePlugins) {
             plugin.notifyAfterInline(inlineMethod);
         }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T getSingleMatchingNode(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz) {
+        if (!hasNonMatchingEntries) {
+            assert returnAndUnwindNodes.size() == 1;
+            return (T) returnAndUnwindNodes.get(0);
+        }
 
-        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL) && DumpDuringGraphBuilding.getValue(options)) {
-            Debug.dump(Debug.INFO_LOG_LEVEL, methodScope.graph, "Inline finished: %s.%s", inlineMethod.getDeclaringClass().getUnqualifiedName(), inlineMethod.getName());
+        for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
+            ControlSinkNode node = returnAndUnwindNodes.get(i);
+            if (clazz.isInstance(node)) {
+                return (T) node;
+            }
         }
+        throw GraalError.shouldNotReachHere();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> List<T> getMatchingNodes(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz, int resultCount) {
+        if (!hasNonMatchingEntries) {
+            return (List<T>) returnAndUnwindNodes;
+        }
+
+        List<T> result = new ArrayList<>(resultCount);
+        for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
+            ControlSinkNode node = returnAndUnwindNodes.get(i);
+            if (clazz.isInstance(node)) {
+                result.add((T) node);
+            }
+        }
+        assert result.size() == resultCount;
+        return result;
     }
 
     private static RuntimeException tooDeepInlining(PEMethodScope methodScope) {
@@ -809,25 +835,27 @@
     }
 
     @Override
-    protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node node) {
+    protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node n) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
+        Node node = n;
         if (node instanceof ParameterNode) {
             ParameterNode param = (ParameterNode) node;
-            if (methodScope.arguments != null) {
+            if (methodScope.isInlinedMethod()) {
                 Node result = methodScope.arguments[param.index()];
                 assert result != null;
                 return result;
 
-            } else if (methodScope.parameterPlugin != null) {
+            } else if (parameterPlugin != null) {
+                assert !methodScope.isInlinedMethod();
                 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null);
-                Node result = methodScope.parameterPlugin.interceptParameter(graphBuilderContext, param.index(),
+                Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(),
                                 StampPair.create(param.stamp(), param.uncheckedStamp()));
                 if (result != null) {
                     return result;
                 }
             }
-
+            node = param.copyWithInputs();
         }
 
         return super.handleFloatingNodeBeforeAdd(methodScope, loopScope, node);
@@ -841,7 +869,7 @@
             }
 
             JavaKind invokeReturnKind = methodScope.invokeData.invoke.asNode().getStackKind();
-            FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null);
+            FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null);
 
             /*
              * When the encoded graph has methods inlining, we can already have a proper caller
@@ -866,7 +894,7 @@
             ensureStateAfterDecoded(methodScope);
 
             assert methodScope.exceptionPlaceholderNode == null;
-            methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode());
+            methodScope.exceptionPlaceholderNode = graph.add(new ExceptionPlaceholderNode());
             registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false);
             FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Thu Apr 06 14:31:32 2017 -0700
@@ -322,7 +322,7 @@
 
                 finalizeGraph(graph);
 
-                Debug.dump(Debug.INFO_LOG_LEVEL, graph, "%s: Final", method.getName());
+                Debug.dump(Debug.INFO_LEVEL, graph, "%s: Final", method.getName());
 
                 return graph;
             } catch (Throwable e) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,6 +25,7 @@
 import static java.util.FormattableFlags.ALTERNATE;
 import static org.graalvm.compiler.core.common.LocationIdentity.any;
 import static org.graalvm.compiler.debug.Debug.applyFormattingFlagsAndWidth;
+import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugStubsAndSnippets;
 import static org.graalvm.compiler.graph.iterators.NodePredicates.isNotA;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
@@ -57,7 +58,9 @@
 import org.graalvm.compiler.core.common.type.TypeReference;
 import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugConfig;
 import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.debug.DebugTimer;
 import org.graalvm.compiler.debug.GraalError;
@@ -82,8 +85,8 @@
 import org.graalvm.compiler.nodes.MergeNode;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PhiNode;
-import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.PiNode.Placeholder;
+import org.graalvm.compiler.nodes.PiNode.PlaceholderStamp;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StartNode;
 import org.graalvm.compiler.nodes.StateSplit;
@@ -632,7 +635,8 @@
             SnippetTemplate template = Options.UseSnippetTemplateCache.getValue(options) && args.cacheable ? templates.get(args.cacheKey) : null;
             if (template == null) {
                 SnippetTemplates.increment();
-                try (DebugCloseable a = SnippetTemplateCreationTime.start(); Scope s = Debug.scope("SnippetSpecialization", args.info.method)) {
+                DebugConfig config = DebugStubsAndSnippets.getValue(options) ? DebugScope.getConfig() : Debug.silentConfig();
+                try (DebugCloseable a = SnippetTemplateCreationTime.start(); Scope s = Debug.sandbox("SnippetSpecialization", config, args.info.method)) {
                     template = new SnippetTemplate(options, providers, snippetReflection, args);
                     if (Options.UseSnippetTemplateCache.getValue(options) && args.cacheable) {
                         templates.put(args.cacheKey, template);
@@ -744,7 +748,7 @@
             }
             snippetCopy.addDuplicates(snippetGraph.getNodes(), snippetGraph, snippetGraph.getNodeCount(), nodeReplacements);
 
-            Debug.dump(Debug.INFO_LOG_LEVEL, snippetCopy, "Before specialization");
+            Debug.dump(Debug.INFO_LEVEL, snippetCopy, "Before specialization");
 
             // Gather the template parameters
             parameters = new Object[parameterCount];
@@ -772,10 +776,10 @@
                         for (Node usage : placeholder.usages().snapshot()) {
                             if (usage instanceof LoadIndexedNode) {
                                 LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
-                                Debug.dump(Debug.INFO_LOG_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
+                                Debug.dump(Debug.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
                                 LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp()));
                                 snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
-                                Debug.dump(Debug.INFO_LOG_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
+                                Debug.dump(Debug.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
                             } else if (usage instanceof StoreIndexedNode) {
                                 /*
                                  * The template lowering doesn't really treat this as an array so
@@ -813,11 +817,15 @@
 
             ArrayList<StateSplit> curSideEffectNodes = new ArrayList<>();
             ArrayList<DeoptimizingNode> curDeoptNodes = new ArrayList<>();
-            ArrayList<ValueNode> curStampNodes = new ArrayList<>();
+            ArrayList<ValueNode> curPlaceholderStampedNodes = new ArrayList<>();
             for (Node node : snippetCopy.getNodes()) {
-                if (node instanceof ValueNode && ((ValueNode) node).stamp() == StampFactory.forNodeIntrinsic()) {
-                    curStampNodes.add((ValueNode) node);
+                if (node instanceof ValueNode) {
+                    ValueNode valueNode = (ValueNode) node;
+                    if (valueNode.stamp() == PlaceholderStamp.singleton()) {
+                        curPlaceholderStampedNodes.add(valueNode);
+                    }
                 }
+
                 if (node instanceof StateSplit) {
                     StateSplit stateSplit = (StateSplit) node;
                     FrameState frameState = stateSplit.stateAfter();
@@ -889,7 +897,7 @@
                     this.memoryAnchor = null;
                 }
             }
-            Debug.dump(Debug.INFO_LOG_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
+            Debug.dump(Debug.INFO_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
 
             List<ReturnNode> returnNodes = snippet.getNodes(ReturnNode.TYPE).snapshot();
             if (returnNodes.isEmpty()) {
@@ -924,7 +932,7 @@
 
             this.sideEffectNodes = curSideEffectNodes;
             this.deoptNodes = curDeoptNodes;
-            this.stampNodes = curStampNodes;
+            this.placeholderStampedNodes = curPlaceholderStampedNodes;
 
             nodes = new ArrayList<>(snippet.getNodeCount());
             for (Node node : snippet.getNodes()) {
@@ -934,7 +942,7 @@
             }
 
             Debug.counter("SnippetTemplateNodeCount[%#s]", args).add(nodes.size());
-            Debug.dump(Debug.INFO_LOG_LEVEL, snippet, "SnippetTemplate final state");
+            Debug.dump(Debug.INFO_LEVEL, snippet, "SnippetTemplate final state");
 
         } catch (Throwable ex) {
             throw Debug.handle(ex);
@@ -1043,9 +1051,9 @@
     private final ArrayList<DeoptimizingNode> deoptNodes;
 
     /**
-     * The nodes that inherit the {@link ValueNode#stamp()} from the replacee during instantiation.
+     * Nodes that have a stamp originating from a {@link Placeholder}.
      */
-    private final ArrayList<ValueNode> stampNodes;
+    private final ArrayList<ValueNode> placeholderStampedNodes;
 
     /**
      * The nodes to be inlined when this specialization is instantiated.
@@ -1364,6 +1372,21 @@
      */
     @SuppressWarnings("try")
     public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args) {
+        return instantiate(metaAccess, replacee, replacer, args, true);
+    }
+
+    /**
+     * Replaces a given fixed node with this specialized snippet.
+     *
+     * @param metaAccess
+     * @param replacee the node that will be replaced
+     * @param replacer object that replaces the usages of {@code replacee}
+     * @param args the arguments to be bound to the flattened positional parameters of the snippet
+     * @param killReplacee is true, the replacee node is deleted
+     * @return the map of duplicated nodes (original -&gt; duplicate)
+     */
+    @SuppressWarnings("try")
+    public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args, boolean killReplacee) {
         assert assertSnippetKills(replacee);
         try (DebugCloseable a = args.info.instantiationTimer.start(); DebugCloseable b = instantiationTimer.start()) {
             args.info.instantiationCounter.increment();
@@ -1375,7 +1398,7 @@
             EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
             replacements.put(entryPointNode, AbstractBeginNode.prevBegin(replacee));
             UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             // Re-wire the control flow graph around the replacee
             FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
@@ -1458,10 +1481,12 @@
                 }
             }
 
-            // Remove the replacee from its graph
-            GraphUtil.killCFG(replacee);
+            if (killReplacee) {
+                // Remove the replacee from its graph
+                GraphUtil.killCFG(replacee);
+            }
 
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
             return duplicates;
         }
     }
@@ -1478,14 +1503,14 @@
     }
 
     private void updateStamps(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
-        for (ValueNode stampNode : stampNodes) {
-            Node stampDup = duplicates.get(stampNode);
-            if (stampDup instanceof PiNode.Placeholder) {
-                PiNode.Placeholder placeholder = (Placeholder) stampDup;
-                PiNode pi = placeholder.getReplacement(replacee.stamp());
-                placeholder.replaceAndDelete(pi);
+        for (ValueNode node : placeholderStampedNodes) {
+            ValueNode dup = (ValueNode) duplicates.get(node);
+            Stamp replaceeStamp = replacee.stamp();
+            if (node instanceof Placeholder) {
+                Placeholder placeholderDup = (Placeholder) dup;
+                placeholderDup.makeReplacement(replaceeStamp);
             } else {
-                ((ValueNode) stampDup).setStamp(replacee.stamp());
+                dup.setStamp(replaceeStamp);
             }
         }
         for (ParameterNode paramNode : snippet.getNodes(ParameterNode.TYPE)) {
@@ -1526,7 +1551,7 @@
             EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
             replacements.put(entryPointNode, tool.getCurrentGuardAnchor().asNode());
             UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             FixedWithNextNode lastFixedNode = tool.lastFixedNode();
             assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph + " lastFixed=" + lastFixedNode;
@@ -1550,7 +1575,7 @@
                 returnDuplicate.replaceAndDelete(next);
             }
 
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
         }
     }
 
@@ -1588,7 +1613,7 @@
                 }
             }
             UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(floatingNodes, snippet, floatingNodes.size(), replacements);
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             rewireFrameStates(replacee, duplicates);
             updateStamps(replacee, duplicates);
@@ -1600,7 +1625,7 @@
             ValueNode returnValue = (ValueNode) duplicates.get(returnNode.result());
             replacer.replace(replacee, returnValue);
 
-            Debug.dump(Debug.INFO_LOG_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java	Thu Apr 06 14:31:32 2017 -0700
@@ -42,6 +42,7 @@
 import org.graalvm.compiler.core.common.LocationIdentity;
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.common.calc.UnsignedMath;
+import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
@@ -76,23 +77,23 @@
 import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
 import org.graalvm.compiler.nodes.extended.GetClassNode;
 import org.graalvm.compiler.nodes.extended.MembarNode;
+import org.graalvm.compiler.nodes.extended.RawLoadNode;
+import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.extended.UnboxNode;
-import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.extended.UnsafeMemoryLoadNode;
 import org.graalvm.compiler.nodes.extended.UnsafeMemoryStoreNode;
-import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
 import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode;
-import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode;
 import org.graalvm.compiler.nodes.java.DynamicNewArrayNode;
 import org.graalvm.compiler.nodes.java.DynamicNewInstanceNode;
 import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 import org.graalvm.compiler.nodes.java.RegisterFinalizerNode;
+import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode;
 import org.graalvm.compiler.replacements.nodes.ReverseBytesNode;
@@ -173,7 +174,8 @@
                 @Override
                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                     ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
-                    b.addPush(JavaKind.Object, LoadFieldNode.create(b.getAssumptions(), value, field));
+                    b.addPush(JavaKind.Object, LoadFieldNode.create(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(),
+                                    b.getOptions(), b.getAssumptions(), value, field, false, false));
                     return true;
                 }
             });
@@ -557,8 +559,8 @@
             r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() {
                 @Override
                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) {
-                    RawLoadNode value = b.add(new RawLoadNode(node, offset, JavaKind.Object, LocationIdentity.any()));
-                    value.setStamp(StampFactory.object(TypeReference.createTrusted(b.getAssumptions(), metaAccess.lookupJavaType(c))));
+                    ObjectStamp stamp = StampFactory.object(TypeReference.createTrusted(b.getAssumptions(), metaAccess.lookupJavaType(c)));
+                    RawLoadNode value = b.add(new RawLoadNode(stamp, node, offset, LocationIdentity.any(), JavaKind.Object));
                     b.addPush(JavaKind.Object, value);
                     return true;
                 }
@@ -881,8 +883,8 @@
                         if (totalCount == 0) {
                             b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
                         } else if (falseCount == 0 || trueCount == 0) {
-                            boolean expected = falseCount == 0 ? true : false;
-                            LogicNode condition = b.add(IntegerEqualsNode.create(result, b.add(ConstantNode.forBoolean(!expected)), /* constantReflection */ null));
+                            boolean expected = falseCount == 0;
+                            LogicNode condition = b.addWithInputs(IntegerEqualsNode.create(result, b.add(ConstantNode.forBoolean(!expected))));
                             b.append(new FixedGuardNode(condition, DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, true));
                             newResult = b.add(ConstantNode.forBoolean(expected));
                         } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -204,10 +204,7 @@
                         return;
                     }
                     VirtualArrayNode srcVirtual = (VirtualArrayNode) srcAlias;
-                    if (destVirtual.componentType().getJavaKind() != JavaKind.Object) {
-                        return;
-                    }
-                    if (srcVirtual.componentType().getJavaKind() != JavaKind.Object) {
+                    if (destVirtual.componentType().getJavaKind() != srcVirtual.componentType().getJavaKind()) {
                         return;
                     }
                     if (!checkBounds(srcPosInt, len, srcVirtual)) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -174,7 +174,7 @@
                 }
             }
             InliningUtil.inline(invoke, replacementGraph, false, targetMethod);
-            Debug.dump(Debug.INFO_LOG_LEVEL, graph(), "After inlining replacement %s", replacementGraph);
+            Debug.dump(Debug.DETAILED_LEVEL, graph(), "After inlining replacement %s", replacementGraph);
         } else {
             if (isPlaceholderBci(invoke.bci())) {
                 throw new GraalError("%s: cannot lower to invoke with placeholder BCI: %s", graph(), this);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -335,8 +335,8 @@
                             adder.add(new ValueAnchorNode(newGuard));
                             guard = newGuard;
                         }
-                        PiNode piNode = adder.add(new PiNode(argument, StampFactory.object(targetType), guard.asNode()));
-                        arguments[index] = piNode;
+                        ValueNode valueNode = adder.add(PiNode.create(argument, StampFactory.object(targetType), guard.asNode()));
+                        arguments[index] = valueNode;
                     } else {
                         inst.safeDelete();
                     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java	Thu Apr 06 14:31:32 2017 -0700
@@ -26,6 +26,7 @@
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
 
 import org.graalvm.compiler.core.common.LIRKind;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -71,8 +72,8 @@
         this.incoming = incoming;
     }
 
-    public ReadRegisterNode(Register register, boolean directUse, boolean incoming) {
-        super(TYPE, StampFactory.forNodeIntrinsic());
+    public ReadRegisterNode(@InjectedNodeParameter Stamp stamp, Register register, boolean directUse, boolean incoming) {
+        super(TYPE, stamp);
         assert register != null;
         this.register = register;
         this.directUse = directUse;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider.processor/src/org/graalvm/compiler/serviceprovider/processor/ServiceProviderProcessor.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider.processor/src/org/graalvm/compiler/serviceprovider/processor/ServiceProviderProcessor.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,7 +25,10 @@
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import javax.annotation.processing.AbstractProcessor;
@@ -53,6 +56,7 @@
 public class ServiceProviderProcessor extends AbstractProcessor {
 
     private final Set<TypeElement> processed = new HashSet<>();
+    private final Map<TypeElement, String> serviceProviders = new HashMap<>();
 
     @Override
     public SourceVersion getSupportedSourceVersion() {
@@ -82,23 +86,22 @@
             } catch (MirroredTypeException ex) {
                 TypeMirror serviceInterface = ex.getTypeMirror();
                 if (verifyAnnotation(serviceInterface, serviceProvider)) {
-                    String interfaceName = ex.getTypeMirror().toString();
-                    createProviderFile(serviceProvider, interfaceName);
+                    if (serviceProvider.getNestingKind().isNested()) {
+                        /*
+                         * This is a simplifying constraint that means we don't have to process the
+                         * qualified name to insert '$' characters at the relevant positions.
+                         */
+                        String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName());
+                        processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
+                    } else {
+                        serviceProviders.put(serviceProvider, ex.getTypeMirror().toString());
+                    }
                 }
             }
         }
     }
 
-    private void createProviderFile(TypeElement serviceProvider, String interfaceName) {
-        if (serviceProvider.getNestingKind().isNested()) {
-            // This is a simplifying constraint that means we don't have to
-            // processed the qualified name to insert '$' characters at
-            // the relevant positions.
-            String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName());
-            processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
-            return;
-        }
-
+    private void writeProviderFile(TypeElement serviceProvider, String interfaceName) {
         String filename = "META-INF/providers/" + serviceProvider.getQualifiedName();
         try {
             FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, serviceProvider);
@@ -114,7 +117,7 @@
      * Determines if a given exception is (most likely) caused by
      * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599">Bug 367599</a>.
      */
-    public static boolean isBug367599(Throwable t) {
+    private static boolean isBug367599(Throwable t) {
         if (t instanceof FilerException) {
             for (StackTraceElement ste : t.getStackTrace()) {
                 if (ste.toString().contains("org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.create")) {
@@ -123,15 +126,16 @@
                 }
             }
         }
-        if (t.getCause() != null) {
-            return isBug367599(t.getCause());
-        }
-        return false;
+        return t.getCause() != null && isBug367599(t.getCause());
     }
 
     @Override
     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
         if (roundEnv.processingOver()) {
+            for (Entry<TypeElement, String> e : serviceProviders.entrySet()) {
+                writeProviderFile(e.getKey(), e.getValue());
+            }
+            serviceProviders.clear();
             return true;
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java	Thu Apr 06 14:31:32 2017 -0700
@@ -124,8 +124,6 @@
             Class<?> expectedClass = expected.getClass();
             Class<?> actualClass = actual.getClass();
             if (expectedClass.isArray()) {
-                Assert.assertTrue(message, expected != null);
-                Assert.assertTrue(message, actual != null);
                 Assert.assertEquals(message, expectedClass, actual.getClass());
                 if (expected instanceof int[]) {
                     Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java	Thu Apr 06 14:31:32 2017 -0700
@@ -25,8 +25,13 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Formatter;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.graalvm.util.CollectionsUtil;
 
 /**
  * Utility methods for spawning a VM in a subprocess during unit tests.
@@ -54,6 +59,63 @@
     }
 
     /**
+     * Pattern for a single shell command argument that does not need to quoted.
+     */
+    private static final Pattern SAFE_SHELL_ARG = Pattern.compile("[A-Za-z0-9@%_\\-\\+=:,\\./]+");
+
+    /**
+     * Reliably quote a string as a single shell command argument.
+     */
+    public static String quoteShellArg(String arg) {
+        if (arg.isEmpty()) {
+            return "\"\"";
+        }
+        Matcher m = SAFE_SHELL_ARG.matcher(arg);
+        if (m.matches()) {
+            return arg;
+        }
+        // See http://stackoverflow.com/a/1250279
+        return "'" + arg.replace("'", "'\"'\"'") + "'";
+    }
+
+    /**
+     * Formats an executed shell command followed by its output. The command is formatted such that
+     * it can be copy and pasted into a console for re-execution.
+     *
+     * @param command the list containing the program and its arguments
+     * @param outputLines the output of the command broken into lines
+     * @param header if non null, the returned string has a prefix of this value plus a newline
+     * @param trailer if non null, the returned string has a suffix of this value plus a newline
+     */
+    public static String formatExecutedCommand(List<String> command, List<String> outputLines, String header, String trailer) {
+        Formatter msg = new Formatter();
+        if (header != null) {
+            msg.format("%s%n", header);
+        }
+        msg.format("%s%n", CollectionsUtil.mapAndJoin(command, e -> quoteShellArg(String.valueOf(e)), " "));
+        for (String line : outputLines) {
+            msg.format("%s%n", line);
+        }
+        if (trailer != null) {
+            msg.format("%s%n", trailer);
+        }
+        return msg.toString();
+    }
+
+    /**
+     * Returns a new copy {@code args} with debugger arguments removed.
+     */
+    public static List<String> withoutDebuggerArguments(List<String> args) {
+        List<String> result = new ArrayList<>(args.size());
+        for (String arg : args) {
+            if (!(arg.equals("-Xdebug") || arg.startsWith("-Xrunjdwp:"))) {
+                result.add(arg);
+            }
+        }
+        return result;
+    }
+
+    /**
      * Gets the command line used to start the current Java VM, including all VM arguments, but not
      * including the main class or any Java arguments. This can be used to spawn an identical VM,
      * but running different Java code.
@@ -68,9 +130,28 @@
         }
     }
 
-    public static final List<String> JVM_OPTIONS_WITH_ONE_ARG = System.getProperty("java.specification.version").compareTo("1.9") < 0 ? //
-                    Arrays.asList("-cp", "-classpath") : //
-                    Arrays.asList("-cp", "-classpath", "-mp", "-modulepath", "-upgrademodulepath", "-addmods", "-m", "-limitmods");
+    private static final boolean isJava8OrEarlier = System.getProperty("java.specification.version").compareTo("1.9") < 0;
+
+    private static boolean hasArg(String optionName) {
+        if (optionName.equals("-cp") || optionName.equals("-classpath")) {
+            return true;
+        }
+        if (!isJava8OrEarlier) {
+            if (optionName.equals("--version") ||
+                            optionName.equals("--show-version") ||
+                            optionName.equals("--dry-run") ||
+                            optionName.equals("--disable-@files") ||
+                            optionName.equals("--dry-run") ||
+                            optionName.equals("--help") ||
+                            optionName.equals("--help-extra")) {
+                return false;
+            }
+            if (optionName.startsWith("--")) {
+                return optionName.indexOf('=') == -1;
+            }
+        }
+        return false;
+    }
 
     private static int findMainClassIndex(List<String> commandLine) {
         int i = 1; // Skip the java executable
@@ -79,7 +160,7 @@
             String s = commandLine.get(i);
             if (s.charAt(0) != '-') {
                 return i;
-            } else if (JVM_OPTIONS_WITH_ONE_ARG.contains(s)) {
+            } else if (hasArg(s)) {
                 i += 2;
             } else {
                 i++;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java	Thu Apr 06 14:31:32 2017 -0700
@@ -194,7 +194,7 @@
             Debug.log(" ==== cfg kill effects");
             effects.apply(graph, obsoleteNodes, true);
         }
-        Debug.dump(Debug.VERBOSE_LOG_LEVEL, graph, "After applying effects");
+        Debug.dump(Debug.DETAILED_LEVEL, graph, "After applying effects");
         assert VirtualUtil.assertNonReachable(graph, obsoleteNodes);
         for (Node node : obsoleteNodes) {
             if (node.isAlive() && node.hasNoUsages()) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java	Thu Apr 06 14:31:32 2017 -0700
@@ -100,8 +100,8 @@
                             closure.applyEffects();
                         }
 
-                        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
-                            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "%s iteration", getName());
+                        if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
+                            Debug.dump(Debug.DETAILED_LEVEL, graph, "%s iteration", getName());
                         }
 
                         new DeadCodeEliminationPhase(Required).apply(graph);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java	Thu Apr 06 14:31:32 2017 -0700
@@ -118,6 +118,7 @@
     private ObjectState[] getObjectStateArrayForModification() {
         if (arrayRefCount.refCount > 1) {
             objectStates = objectStates.clone();
+            arrayRefCount.refCount--;
             arrayRefCount = new RefCount();
         }
         return objectStates;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java	Thu Apr 06 14:31:32 2017 -0700
@@ -23,7 +23,7 @@
 package org.graalvm.compiler.virtual.phases.ea;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.IntFunction;
@@ -432,15 +432,16 @@
             counter.increment();
             VirtualObjectNode virtual = virtualObjects.get(object);
             state.materializeBefore(materializeBefore, virtual, effects);
-            updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue());
+            assert !updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()) : "method must already have been called before";
             return true;
         } else {
             return false;
         }
     }
 
-    public static void updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) {
+    public static boolean updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) {
         // update all existing states with the newly materialized object
+        boolean change = false;
         for (int i = 0; i < state.getStateCount(); i++) {
             ObjectState objState = state.getObjectStateOptional(i);
             if (objState != null && objState.isVirtual()) {
@@ -448,10 +449,12 @@
                 for (int i2 = 0; i2 < entries.length; i2++) {
                     if (entries[i2] == virtual) {
                         state.setEntry(i, i2, materializedValue);
+                        change = true;
                     }
                 }
             }
         }
+        return change;
     }
 
     @Override
@@ -470,25 +473,25 @@
             int length = initialState.getStateCount();
 
             boolean change;
-            boolean[] ensureVirtualized = new boolean[length];
+            BitSet ensureVirtualized = new BitSet(length);
             for (int i = 0; i < length; i++) {
                 ObjectState state = initialState.getObjectStateOptional(i);
                 if (state != null && state.isVirtual() && state.getEnsureVirtualized()) {
-                    ensureVirtualized[i] = true;
+                    ensureVirtualized.set(i);
                 }
             }
             do {
                 // propagate "ensureVirtualized" flag
                 change = false;
                 for (int i = 0; i < length; i++) {
-                    if (!ensureVirtualized[i]) {
+                    if (!ensureVirtualized.get(i)) {
                         ObjectState state = initialState.getObjectStateOptional(i);
                         if (state != null && state.isVirtual()) {
                             for (ValueNode entry : state.getEntries()) {
                                 if (entry instanceof VirtualObjectNode) {
-                                    if (ensureVirtualized[((VirtualObjectNode) entry).getObjectId()]) {
+                                    if (ensureVirtualized.get(((VirtualObjectNode) entry).getObjectId())) {
                                         change = true;
-                                        ensureVirtualized[i] = true;
+                                        ensureVirtualized.set(i);
                                         break;
                                     }
                                 }
@@ -500,7 +503,7 @@
 
             for (int i = 0; i < length; i++) {
                 ObjectState state = initialState.getObjectStateOptional(i);
-                if (state != null && state.isVirtual() && !ensureVirtualized[i]) {
+                if (state != null && state.isVirtual() && !ensureVirtualized.get(i)) {
                     initialState.materializeBefore(end, virtualObjects.get(i), blockEffects.get(loopPredecessor));
                 }
             }
@@ -690,7 +693,6 @@
                 } else {
 
                     for (int object : virtualObjTemp) {
-
                         if (PartialEscapeBlockState.identicalObjectStates(states, object)) {
                             newState.addObject(object, states[0].getObjectState(object).share());
                             continue;
@@ -745,7 +747,7 @@
                 for (PhiNode phi : getPhis()) {
                     aliases.set(phi, null);
                     if (hasVirtualInputs.isMarked(phi) && phi instanceof ValuePhiNode) {
-                        materialized |= processPhi((ValuePhiNode) phi, states, virtualObjTemp);
+                        materialized |= processPhi((ValuePhiNode) phi, states);
                     }
                 }
                 if (materialized) {
@@ -761,31 +763,35 @@
             for (int i = 1; i < states.length; i++) {
                 length = Math.min(length, states[i].getStateCount());
             }
-            boolean[] result = new boolean[length];
-            Arrays.fill(result, true);
-            int count = length;
-            for (int i = 0; i < states.length; i++) {
-                PartialEscapeBlockState<?> state = states[i];
-                for (int i2 = 0; i2 < length; i2++) {
-                    if (result[i2]) {
-                        if (state.getObjectStateOptional(i2) == null) {
-                            result[i2] = false;
-                            count--;
-                        }
-                    }
+
+            int count = 0;
+            for (int objectIndex = 0; objectIndex < length; objectIndex++) {
+                if (intersectObjectState(states, objectIndex)) {
+                    count++;
                 }
             }
-            int[] resultInts = new int[count];
+
             int index = 0;
-            for (int i = 0; i < length; i++) {
-                if (result[i]) {
-                    resultInts[index++] = i;
+            int[] resultInts = new int[count];
+            for (int objectIndex = 0; objectIndex < length; objectIndex++) {
+                if (intersectObjectState(states, objectIndex)) {
+                    resultInts[index++] = objectIndex;
                 }
             }
             assert index == count;
             return resultInts;
         }
 
+        private boolean intersectObjectState(PartialEscapeBlockState<?>[] states, int objectIndex) {
+            for (int i = 0; i < states.length; i++) {
+                PartialEscapeBlockState<?> state = states[i];
+                if (state.getObjectStateOptional(objectIndex) == null) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         /**
          * Try to merge multiple virtual object states into a single object state. If the incoming
          * object states are compatible, then this method will create PhiNodes for the object's
@@ -960,11 +966,9 @@
          *
          * @param phi the PhiNode that should be processed
          * @param states the predecessor block states of the merge
-         * @param mergedVirtualObjects the set of virtual objects that exist in all incoming states,
-         *            and therefore also exist in the merged state
          * @return true if materialization happened during the merge, false otherwise
          */
-        private boolean processPhi(ValuePhiNode phi, PartialEscapeBlockState<?>[] states, int[] mergedVirtualObjects) {
+        private boolean processPhi(ValuePhiNode phi, PartialEscapeBlockState<?>[] states) {
 
             // determine how many inputs are virtual and if they're all the same virtual object
             int virtualInputs = 0;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualUtil.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualUtil.java	Thu Apr 06 14:31:32 2017 -0700
@@ -115,7 +115,7 @@
         }
         if (!success) {
             TTY.println();
-            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "assertNonReachable");
+            Debug.forceDump(graph, "assertNonReachable");
         }
         return success;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/EconomicMap.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/EconomicMap.java	Thu Apr 06 14:31:32 2017 -0700
@@ -59,7 +59,7 @@
     }
 
     /**
-     * Creates a new map that guarantees insertion order on the key set with the the default
+     * Creates a new map that guarantees insertion order on the key set with the default
      * {@link Equivalence#DEFAULT} comparison strategy for keys.
      */
     static <K, V> EconomicMap<K, V> create() {
@@ -67,7 +67,7 @@
     }
 
     /**
-     * Creates a new map that guarantees insertion order on the key set with the the default
+     * Creates a new map that guarantees insertion order on the key set with the default
      * {@link Equivalence#DEFAULT} comparison strategy for keys and initializes with a specified
      * capacity.
      */
@@ -84,7 +84,7 @@
     }
 
     /**
-     * Creates a new map that guarantees insertion order on the key set with the the default
+     * Creates a new map that guarantees insertion order on the key set with the default
      * {@link Equivalence#DEFAULT} comparison strategy for keys and copies all elements from the
      * specified existing map.
      */