8183991: Update Graal
authoriveresov
Fri, 07 Jul 2017 09:40:47 -0700
changeset 46640 70bdce04c59b
parent 46638 3c5c50af29a7
child 46641 f64dc604ef8d
8183991: Update Graal 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/DataBuilder.java
hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/Main.java
hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py
hotspot/src/jdk.internal.vm.compiler/share/classes/module-info.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.test/src/org/graalvm/compiler/asm/test/AssemblerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.bytecode/src/org/graalvm/compiler/bytecode/BytecodeDisassembler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/BiDirectionalTraceBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/SingleBlockTraceBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceBuilderResult.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/alloc/UniDirectionalTraceBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.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/ConditionalEliminationTestBase.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/DontReuseArgumentSpaceTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.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/GraalCompilerAssumptionsTest.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/GraalDebugHandlersFactoryTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.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/ImplicitNullCheckTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InfopointReasonTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InterfaceMethodHandleTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopFullUnrollTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopUnswitchTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryGraphCanonicalizeTest.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/PushNodesThroughPiTest.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/StaticInterfaceFieldTest.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/UnbalancedMonitorsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyBailoutUsageTest.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/VerifyVirtualizableTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/backend/AllocatorTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/backend/BackendTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest1.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest2.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest3.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest4.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest5.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest6.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTestInterception01.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTestInterception02.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/VerifyMethodMetricsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/MonitorDeoptTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/SynchronizedMethodDeoptimizationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PoorMansEATest.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/InvokeGraal.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/StaticAnalysis.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThread.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThreadFactory.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/GraalDebugInitializationParticipant.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/LIRGenerationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchContext.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchPattern.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchRuleRegistry.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchStatement.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyCompilerConfiguration.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.core/src/org/graalvm/compiler/core/phases/MidTier.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.testLogging.input
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugHistogramTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugTimerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/TimerKeyTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/AbstractKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/AccumulatedKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CSVUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CloseableCounter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CounterKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CounterKeyImpl.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/DebugCloseable.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfig.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigCustomizer.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigScope.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugCounter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugEnvironment.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/DebugHandler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHandlersFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHistogram.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugInitializationParticipant.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugMemUseTracker.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugMethodMetrics.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.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/DebugTimer.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugValueFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugValueMap.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DelegatingDebugConfig.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GlobalMetrics.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/Indent.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/JavaMethodContext.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/KeyRegistry.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MemUseTrackerKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MemUseTrackerKeyImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MetricKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/ScopeImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/TimerKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/TimerKeyImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/TopLevelDebugConfig.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/UniquePathUtilities.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/AccumulatedDebugValue.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/CloseableCounterImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/CounterImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramAsciiPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramRPrinter.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.debug/src/org/graalvm/compiler/debug/internal/DebugValue.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugValueMap.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugValuesPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/KeyRegistry.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/MemUseTrackerImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/TimerImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsInlineeScopeInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsRootScopeInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/GraphTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeMapTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeUsagesTests.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeValidationChecksTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest2.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/CachedGraph.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/NodeClass.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.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotBackend.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLoweringProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotBackend.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.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackend.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.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/CompileTheWorld.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/GraalOSRLockTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotCryptoSubstitutionTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalCompilerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMonitorValueTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotNodeSubstitutionsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/InstalledCodeExecuteHelperTest.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/MemoryUsageBenchmark.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.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/TestIntrinsicCompiles.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.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.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/FingerprintUtil.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/HotSpotGraalMBean.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/HotSpotHostBackend.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotReplacementsImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotRetryableCompilation.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.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/debug/BenchmarkCounters.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/lir/HotSpotZapRegistersPhase.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/HotSpotAOTProfilingPlugin.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotForeignCallsProviderImpl.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/HotSpotLoweringProvider.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/phases/OnStackReplacementPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/AOTInliningPolicy.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/AssertionSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HashCodeSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.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/MonitorSnippets.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/ObjectCloneNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/StringToBytesSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/UnsafeLoadSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/aot/ResolveConstantSnippets.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/replacements/arraycopy/UnsafeArrayCopySnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/CreateExceptionStub.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/SnippetStub.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.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/FrameStateBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/JTTTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/threads/Object_wait03.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64PrefetchOp.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/phases/StackMoveOptimizationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.jtt/src/org/graalvm/compiler/lir/jtt/SPARCBranchBailoutTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/CompositeValue.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ControlFlowOptimizer.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIR.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRInstruction.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/alloc/lsra/IntervalWalker.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/LinearScanAllocationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanAssignLocationsPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.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/LinearScanRegisterAllocationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanResolveDataFlowPhase.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/lsra/MoveResolver.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/OptimizingLinearScanWalker.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/RegisterVerifier.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScan.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanEliminateSpillMovePhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanLifetimeAnalysisPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanResolveDataFlowPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/DefaultTraceRegisterAllocationPolicy.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/GlobalLivenessInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceAllocationPhase.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/TraceGlobalMoveResolutionPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceGlobalMoveResolver.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/TraceRegisterAllocationPolicy.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/bu/BottomUpAllocator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAllocationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.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/TraceLinearScanRegisterAllocationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.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/alloc/trace/lsra/TraceLocalMoveResolver.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/asm/CompilationResultBuilderFactory.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/debug/LIRGenerationDebugContext.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/dfa/LocationMarker.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/framemap/FrameMapBuilderImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/EconomyAllocationStage.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/FixPointIntervalBuilder.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.lir/src/org/graalvm/compiler/lir/stackslotalloc/SimpleStackSlotAllocator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/StackSlotAllocatorUtil.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/LoopPartialUnrollPhase.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/LoopTransformations.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.phases/src/org/graalvm/compiler/loop/phases/ReassociateInvariantPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentWhole.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopPolicies.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopsData.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/GraphCopyBenchmark.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraphState.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/TraceBuilderBenchmark.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/StaticFieldAccessTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractEndNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractMergeNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/DeoptimizeNode.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/IfNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.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/cfg/Block.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/graphbuilderconf/GraphBuilderTool.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.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/UniquePathUtilities.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.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/ConditionalEliminationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConvertDeoptimizeToGuardPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DeadCodeEliminationPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/GuardLoweringPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/UseTrappingNullChecksPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/AbstractInlineInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/GreedyInliningPolicy.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/LazyName.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/OptimisticOptimizations.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/NodeCostUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/MemoryScheduleVerification.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/GraphOrder.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/BinaryGraphPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugConfigCustomizer.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.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.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64ConvertSnippets.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledExceptionHandlerTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledNullPointerExceptionTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DerivedOopTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EdgesTest.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/PointerTrackingTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SnippetsTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/WordTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.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/ConstantStringIndexOfSnippets.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/InstanceOfSnippetsTemplates.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.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/SnippetCounterNode.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/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.test/src/org/graalvm/compiler/test/GraalTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.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/GraphEffectList.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.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/ReadEliminationClosure.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.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/Word.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordOperationPlugin.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptors.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerBase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerUtils.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/WordFactory.java
hotspot/test/compiler/aot/scripts/build-bootmodules.sh
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTBackend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTBackend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,7 @@
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.GraalCompiler;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.HotSpotBackend;
 import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -82,6 +81,10 @@
         return backend;
     }
 
+    public HotSpotProviders getProviders() {
+        return providers;
+    }
+
     private Suites getSuites() {
         // create suites every time, as we modify options for the compiler
         return backend.getSuites().getDefaultSuites(graalOptions);
@@ -93,10 +96,10 @@
     }
 
     @SuppressWarnings("try")
-    public CompilationResult compileMethod(ResolvedJavaMethod resolvedMethod) {
-        StructuredGraph graph = buildStructuredGraph(resolvedMethod);
+    public CompilationResult compileMethod(ResolvedJavaMethod resolvedMethod, DebugContext debug) {
+        StructuredGraph graph = buildStructuredGraph(resolvedMethod, debug);
         if (graph != null) {
-            return compileGraph(resolvedMethod, graph);
+            return compileGraph(resolvedMethod, graph, debug);
         }
         return null;
     }
@@ -105,12 +108,13 @@
      * Build a structured graph for the member.
      *
      * @param javaMethod method for whose code the graph is to be created
+     * @param debug
      * @return structured graph
      */
     @SuppressWarnings("try")
-    private StructuredGraph buildStructuredGraph(ResolvedJavaMethod javaMethod) {
-        try (Scope s = Debug.scope("AOTParseMethod")) {
-            StructuredGraph graph = new StructuredGraph.Builder(graalOptions).method(javaMethod).useProfilingInfo(false).build();
+    private StructuredGraph buildStructuredGraph(ResolvedJavaMethod javaMethod, DebugContext debug) {
+        try (DebugContext.Scope s = debug.scope("AOTParseMethod")) {
+            StructuredGraph graph = new StructuredGraph.Builder(graalOptions, debug).method(javaMethod).useProfilingInfo(false).build();
             graphBuilderSuite.apply(graph, highTierContext);
             return graph;
         } catch (Throwable e) {
@@ -120,8 +124,8 @@
     }
 
     @SuppressWarnings("try")
-    private CompilationResult compileGraph(ResolvedJavaMethod resolvedMethod, StructuredGraph graph) {
-        try (Scope s = Debug.scope("AOTCompileMethod")) {
+    private CompilationResult compileGraph(ResolvedJavaMethod resolvedMethod, StructuredGraph graph, DebugContext debug) {
+        try (DebugContext.Scope s = debug.scope("AOTCompileMethod")) {
             ProfilingInfo profilingInfo = DefaultProfilingInfo.get(TriState.FALSE);
 
             final boolean isImmutablePIC = true;
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTCompilationTask.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/AOTCompilationTask.java	Fri Jul 07 09:40:47 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -26,14 +26,15 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.GraalCompilerOptions;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugEnvironment;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Management;
 import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.DebugScope;
+import org.graalvm.compiler.debug.DebugContext.Activation;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 
 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
@@ -88,15 +89,12 @@
     /**
      * Compile a method or a constructor.
      */
+    @SuppressWarnings("try")
     public void run() {
         // Ensure a JVMCI runtime is initialized prior to Debug being initialized as the former
         // may include processing command line options used by the latter.
         HotSpotJVMCIRuntime.runtime();
 
-        // Ensure a debug configuration for this thread is initialized
-        if (Debug.isEnabled() && DebugScope.getConfig() == null) {
-            DebugEnvironment.ensureInitialized(graalOptions);
-        }
         AOTCompiler.logCompilation(MiscUtils.uniqueMethodName(method), "Compiling");
 
         final long threadId = Thread.currentThread().getId();
@@ -117,8 +115,12 @@
             allocatedBytesBefore = 0L;
         }
 
+        CompilationResult compResult = null;
         final long startTime = System.currentTimeMillis();
-        CompilationResult compResult = aotBackend.compileMethod(method);
+        SnippetReflectionProvider snippetReflection = aotBackend.getProviders().getSnippetReflection();
+        try (DebugContext debug = DebugContext.create(graalOptions, new GraalDebugHandlersFactory(snippetReflection)); Activation a = debug.activate()) {
+            compResult = aotBackend.compileMethod(method, debug);
+        }
         final long endTime = System.currentTimeMillis();
 
         if (printAfterCompilation || printCompilation) {
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/DataBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/DataBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,8 +33,7 @@
 import jdk.tools.jaotc.binformat.HeaderContainer;
 import jdk.tools.jaotc.utils.Timer;
 import org.graalvm.compiler.code.CompilationResult;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.HotSpotHostBackend;
 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
 import org.graalvm.compiler.hotspot.stubs.Stub;
@@ -124,10 +123,12 @@
     /**
      * Prepare data with all compiled classes and stubs.
      *
+     * @param debug
+     *
      * @throws Exception
      */
     @SuppressWarnings("try")
-    public void prepareData() throws Exception {
+    public void prepareData(DebugContext debug) throws Exception {
         try (Timer t = new Timer(main, "Parsing compiled code")) {
             /*
              * Copy compiled code into code section container and calls stubs (PLT trampoline).
@@ -142,7 +143,7 @@
             }
         }
 
-        AOTCompiledClass stubCompiledCode = retrieveStubCode();
+        AOTCompiledClass stubCompiledCode = retrieveStubCode(debug);
 
         // Free memory!
         try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
@@ -177,18 +178,20 @@
 
     /**
      * Get all stubs from Graal and add them to the code section.
+     *
+     * @param debug
      */
     @SuppressWarnings("try")
-    private AOTCompiledClass retrieveStubCode() {
+    private AOTCompiledClass retrieveStubCode(DebugContext debug) {
         ArrayList<CompiledMethodInfo> stubs = new ArrayList<>();
         HotSpotForeignCallsProvider foreignCallsProvider = backend.getProviders().getForeignCalls();
         for (Stub stub : foreignCallsProvider.getStubs()) {
-            try (Scope scope = Debug.scope("CompileStubs")) {
-                CompilationResult result = stub.getCompilationResult(backend);
+            try (DebugContext.Scope scope = debug.scope("CompileStubs")) {
+                CompilationResult result = stub.getCompilationResult(debug, backend);
                 CompiledMethodInfo cm = new CompiledMethodInfo(result, new AOTStub(stub, backend));
                 stubs.add(cm);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
         AOTCompiledClass stubCompiledCode = new AOTCompiledClass(stubs);
--- a/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/Main.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/Main.java	Fri Jul 07 09:40:47 2017 -0700
@@ -59,7 +59,10 @@
 import jdk.tools.jaotc.collect.module.ModuleSourceProvider;
 import jdk.tools.jaotc.utils.Timer;
 
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Activation;
 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory;
@@ -72,6 +75,7 @@
 import org.graalvm.compiler.phases.BasePhase;
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
@@ -467,6 +471,7 @@
             }
 
             AOTBackend aotBackend = new AOTBackend(this, graalOptions, backend, filters);
+            SnippetReflectionProvider snippetReflection = aotBackend.getProviders().getSnippetReflection();
             AOTCompiler compiler = new AOTCompiler(this, graalOptions, aotBackend, options.threads);
             classes = compiler.compileClasses(classes);
 
@@ -485,7 +490,10 @@
 
             BinaryContainer binaryContainer = new BinaryContainer(graalOptions, graalHotSpotVMConfig, graphBuilderConfig, JVM_VERSION);
             DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer);
-            dataBuilder.prepareData();
+
+            try (DebugContext debug = DebugContext.create(graalOptions, new GraalDebugHandlersFactory(snippetReflection)); Activation a = debug.activate()) {
+                dataBuilder.prepareData(debug);
+            }
 
             // Print information about section sizes
             printContainerInfo(binaryContainer.getHeaderContainer().getContainer());
--- a/hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py	Fri Jul 07 09:40:47 2017 -0700
@@ -70,15 +70,15 @@
       "exports" : [
         "<package-info>",  # exports all packages containing package-info.java
       ],
-      "checkstyle" : "org.graalvm.api.word",
+      "checkstyle" : "org.graalvm.word",
       "javaCompliance" : "1.8",
       "workingSets" : "API,SDK",
     },
-    "org.graalvm.api.word" : {
+    "org.graalvm.word" : {
       "subDir" : "share/classes",
       "sourceDirs" : ["src"],
       "dependencies" : [],
-      "checkstyle" : "org.graalvm.api.word",
+      "checkstyle" : "org.graalvm.word",
       "javaCompliance" : "1.8",
       "workingSets" : "API,SDK",
     },
@@ -142,8 +142,7 @@
       "sourceDirs" : ["src"],
       "checkstyle" : "org.graalvm.compiler.graph",
       "uses" : [
-        "org.graalvm.compiler.debug.DebugConfigCustomizer",
-        "org.graalvm.compiler.debug.DebugInitializationParticipant",
+        "org.graalvm.compiler.debug.DebugHandlersFactory",
         "org.graalvm.compiler.debug.TTYStreamProvider",
       ],
       "dependencies" : [
@@ -792,6 +791,36 @@
       "workingSets" : "Graal,Phases",
     },
 
+    "org.graalvm.compiler.virtual.bench" : {
+      "subDir" : "share/classes",
+      "sourceDirs" : ["src"],
+      "dependencies" : ["mx:JMH_1_18", "org.graalvm.compiler.microbenchmarks"],
+      "checkstyle" : "org.graalvm.compiler.graph",
+      "javaCompliance" : "1.8",
+      "annotationProcessors" : ["mx:JMH_1_18"],
+      "findbugsIgnoresGenerated" : True,
+      "workingSets" : "Graal,Bench",
+      "isTestProject" : True,
+    },
+
+    "org.graalvm.compiler.microbenchmarks" : {
+      "subDir" : "share/classes",
+      "sourceDirs" : ["src"],
+      "dependencies" : [
+        "mx:JMH_1_18",
+        "org.graalvm.compiler.api.test",
+        "org.graalvm.compiler.java",
+        "org.graalvm.compiler.runtime",
+      ],
+      "checkstyle" : "org.graalvm.compiler.graph",
+      "javaCompliance" : "1.8",
+      "checkPackagePrefix" : "false",
+      "annotationProcessors" : ["mx:JMH_1_18"],
+      "findbugsIgnoresGenerated" : True,
+      "workingSets" : "Graal,Bench",
+      "isTestProject" : True,
+    },
+
     "org.graalvm.compiler.loop" : {
       "subDir" : "share/classes",
       "sourceDirs" : ["src"],
@@ -970,7 +999,7 @@
       "sourceDirs" : ["src"],
       "dependencies" : [
         "org.graalvm.compiler.debug",
-        "org.graalvm.api.word",
+        "org.graalvm.word",
       ],
       "annotationProcessors" : ["GRAAL_OPTIONS_PROCESSOR"],
       "checkstyle" : "org.graalvm.compiler.graph",
@@ -998,6 +1027,7 @@
       "subDir" : "share/classes",
       "sourceDirs" : ["src"],
       "dependencies" : [
+        "org.graalvm.compiler.debug",
         "org.graalvm.util",
         "mx:JUNIT",
       ],
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/module-info.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/module-info.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,8 +32,6 @@
 
     uses org.graalvm.compiler.code.DisassemblerProvider;
     uses org.graalvm.compiler.core.match.MatchStatementSet;
-    uses org.graalvm.compiler.debug.DebugConfigCustomizer;
-    uses org.graalvm.compiler.debug.DebugInitializationParticipant;
     uses org.graalvm.compiler.debug.TTYStreamProvider;
     uses org.graalvm.compiler.hotspot.CompilerConfigurationFactory;
     uses org.graalvm.compiler.hotspot.HotSpotBackendFactory;
@@ -51,7 +49,6 @@
     exports org.graalvm.compiler.core.common            to jdk.aot;
     exports org.graalvm.compiler.core.target            to jdk.aot;
     exports org.graalvm.compiler.debug                  to jdk.aot;
-    exports org.graalvm.compiler.debug.internal         to jdk.aot;
     exports org.graalvm.compiler.graph                  to jdk.aot;
     exports org.graalvm.compiler.hotspot                to jdk.aot;
     exports org.graalvm.compiler.hotspot.meta           to jdk.aot;
@@ -66,6 +63,7 @@
     exports org.graalvm.compiler.options                to jdk.aot;
     exports org.graalvm.compiler.phases                 to jdk.aot;
     exports org.graalvm.compiler.phases.tiers           to jdk.aot;
+    exports org.graalvm.compiler.printer                to jdk.aot;
     exports org.graalvm.compiler.runtime                to jdk.aot;
     exports org.graalvm.compiler.replacements           to jdk.aot;
     exports org.graalvm.compiler.word                   to jdk.aot;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -220,7 +220,7 @@
     @Test
     public void testClone() {
         StructuredGraph g = parseEager("preventPeelSnippet", AllowAssumptions.NO);
-        g.copy();
+        g.copy(g.getDebug());
     }
 
     private static List<NodeCount> getNodeCountAnnotations(StructuredGraph graph) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.asm.aarch64;
 
+import static jdk.vm.ci.aarch64.AArch64.cpuRegisters;
 import static org.graalvm.compiler.asm.aarch64.AArch64Assembler.Instruction.ADD;
 import static org.graalvm.compiler.asm.aarch64.AArch64Assembler.Instruction.ADDS;
 import static org.graalvm.compiler.asm.aarch64.AArch64Assembler.Instruction.ADR;
@@ -1010,6 +1011,100 @@
         loadStoreInstruction(LDRS, rt, address, generalFromSize(targetSize), transferSize);
     }
 
+    public enum PrefetchMode {
+        PLDL1KEEP(0b00000),
+        PLDL1STRM(0b00001),
+        PLDL2KEEP(0b00010),
+        PLDL2STRM(0b00011),
+        PLDL3KEEP(0b00100),
+        PLDL3STRM(0b00101),
+
+        PLIL1KEEP(0b01000),
+        PLIL1STRM(0b01001),
+        PLIL2KEEP(0b01010),
+        PLIL2STRM(0b01011),
+        PLIL3KEEP(0b01100),
+        PLIL3STRM(0b01101),
+
+        PSTL1KEEP(0b10000),
+        PSTL1STRM(0b10001),
+        PSTL2KEEP(0b10010),
+        PSTL2STRM(0b10011),
+        PSTL3KEEP(0b10100),
+        PSTL3STRM(0b10101);
+
+        private final int encoding;
+
+        PrefetchMode(int encoding) {
+            this.encoding = encoding;
+        }
+
+        private static PrefetchMode[] modes = {
+                        PLDL1KEEP,
+                        PLDL1STRM,
+                        PLDL2KEEP,
+                        PLDL2STRM,
+                        PLDL3KEEP,
+                        PLDL3STRM,
+
+                        null,
+                        null,
+
+                        PLIL1KEEP,
+                        PLIL1STRM,
+                        PLIL2KEEP,
+                        PLIL2STRM,
+                        PLIL3KEEP,
+                        PLIL3STRM,
+
+                        null,
+                        null,
+
+                        PSTL1KEEP,
+                        PSTL1STRM,
+                        PSTL2KEEP,
+                        PSTL2STRM,
+                        PSTL3KEEP,
+                        PSTL3STRM
+        };
+
+        public static PrefetchMode lookup(int enc) {
+            assert enc >= 00 && enc < modes.length;
+            return modes[enc];
+        }
+
+        public Register toRegister() {
+            return cpuRegisters.get(encoding);
+        }
+    }
+
+    /*
+     * implements a prefetch at a 64-bit aligned address using a scaled 12 bit or unscaled 9 bit
+     * displacement addressing mode
+     *
+     * @param rt general purpose register. May not be null, zr or stackpointer.
+     *
+     * @param address only displacement addressing modes allowed. May not be null.
+     */
+    public void prfm(AArch64Address address, PrefetchMode mode) {
+        assert (address.getAddressingMode() == AddressingMode.IMMEDIATE_SCALED ||
+                        address.getAddressingMode() == AddressingMode.IMMEDIATE_UNSCALED ||
+                        address.getAddressingMode() == AddressingMode.REGISTER_OFFSET);
+        assert mode != null;
+        final int srcSize = 64;
+        final int transferSize = NumUtil.log2Ceil(srcSize / 8);
+        final Register rt = mode.toRegister();
+        // this looks weird but that's because loadStoreInstruction is weird
+        // instruction select fields are size [31:30], v [26] and opc [25:24]
+        // prfm requires size == 0b11, v == 0b0 and opc == 0b11
+        // passing LDRS ensures opc[1] == 0b1
+        // (n.b. passing LDR/STR makes no difference to opc[1:0]!!)
+        // passing General64 ensures opc[0] == 0b1 and v = 0b0
+        // (n.b. passing General32 ensures opc[0] == 0b0 and v = 0b0)
+        // srcSize 64 ensures size == 0b11
+        loadStoreInstruction(LDRS, rt, address, General64, transferSize);
+    }
+
     /**
      * Stores register rt into memory pointed by address.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -146,10 +146,10 @@
                     return new AddressGenerationPlan(ADD_TO_BASE, REGISTER_OFFSET, needsScratch);
                 }
             } else {
-                if (NumUtil.isSignedNbit(9, displacement)) {
+                if (displacementScalable && NumUtil.isUnsignedNbit(12, scaledDisplacement)) {
+                    return new AddressGenerationPlan(NO_WORK, IMMEDIATE_SCALED, false);
+                } else if (NumUtil.isSignedNbit(9, displacement)) {
                     return new AddressGenerationPlan(NO_WORK, IMMEDIATE_UNSCALED, false);
-                } else if (displacementScalable && NumUtil.isUnsignedNbit(12, scaledDisplacement)) {
-                    return new AddressGenerationPlan(NO_WORK, IMMEDIATE_SCALED, false);
                 } else {
                     boolean needsScratch = !isArithmeticImmediate(displacement);
                     return new AddressGenerationPlan(ADD_TO_BASE, REGISTER_OFFSET, needsScratch);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.test/src/org/graalvm/compiler/asm/test/AssemblerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.test/src/org/graalvm/compiler/asm/test/AssemblerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,8 +31,7 @@
 import org.graalvm.compiler.code.DisassemblerProvider;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 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.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.runtime.RuntimeProvider;
@@ -83,10 +82,12 @@
     @SuppressWarnings("try")
     protected InstalledCode assembleMethod(Method m, CodeGenTest test) {
         ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(m);
-        try (Scope s = Debug.scope("assembleMethod", method, codeCache)) {
+        OptionValues options = getInitialOptions();
+        DebugContext debug = getDebugContext(options);
+        try (DebugContext.Scope s = debug.scope("assembleMethod", method, codeCache)) {
             RegisterConfig registerConfig = codeCache.getRegisterConfig();
             CompilationIdentifier compilationId = backend.getCompilationIdentifier(method);
-            StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).compilationId(compilationId).build();
+            StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).compilationId(compilationId).build();
             CallingConvention cc = backend.newLIRGenerationResult(compilationId, null, null, graph, null).getCallingConvention();
 
             CompilationResult compResult = new CompilationResult();
@@ -95,7 +96,7 @@
             compResult.setTotalFrameSize(0);
             compResult.close();
 
-            InstalledCode code = backend.addInstalledCode(method, asCompilationRequest(compilationId), compResult);
+            InstalledCode code = backend.addInstalledCode(debug, method, asCompilationRequest(compilationId), compResult);
 
             for (DisassemblerProvider dis : GraalServices.load(DisassemblerProvider.class)) {
                 String disasm1 = dis.disassembleCompiledCode(codeCache, compResult);
@@ -105,7 +106,7 @@
             }
             return code;
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.bytecode/src/org/graalvm/compiler/bytecode/BytecodeDisassembler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.bytecode/src/org/graalvm/compiler/bytecode/BytecodeDisassembler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -165,7 +165,7 @@
                 stream.next();
                 opcode = stream.currentBC();
             }
-        } catch (RuntimeException e) {
+        } catch (Throwable e) {
             throw new RuntimeException(String.format("Error disassembling %s%nPartial disassembly:%n%s", method.format("%H.%n(%p)"), buf.toString()), e);
         }
         return buf.toString();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.calc.LeftShiftNode;
@@ -47,12 +48,15 @@
         AMD64AddressNode ret = new AMD64AddressNode(base, offset);
         boolean changed;
         do {
-            changed = improve(ret);
+            changed = improve(base.getDebug(), ret);
         } while (changed);
         return base.graph().unique(ret);
     }
 
-    protected boolean improve(AMD64AddressNode ret) {
+    /**
+     * @param debug
+     */
+    protected boolean improve(DebugContext debug, AMD64AddressNode ret) {
         ValueNode newBase = improveInput(ret, ret.getBase(), 0);
         if (newBase != ret.getBase()) {
             ret.setBase(newBase);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,10 +26,16 @@
 import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.graph.spi.Simplifiable;
+import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.lir.amd64.AMD64AddressValue;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -42,7 +48,7 @@
  * optional.
  */
 @NodeInfo
-public class AMD64AddressNode extends AddressNode implements LIRLowerable {
+public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLowerable {
 
     public static final NodeClass<AMD64AddressNode> TYPE = NodeClass.create(AMD64AddressNode.class);
 
@@ -64,6 +70,28 @@
         this.scale = Scale.Times1;
     }
 
+    public void canonicalizeIndex(SimplifierTool tool) {
+        if (index instanceof AddNode) {
+            AddNode add = (AddNode) index;
+            ValueNode valX = add.getX();
+            if (valX instanceof PhiNode) {
+                PhiNode phi = (PhiNode) valX;
+                if (phi.merge() instanceof LoopBeginNode) {
+                    LoopBeginNode loopNode = (LoopBeginNode) phi.merge();
+                    if (!loopNode.isSimpleLoop()) {
+                        ValueNode valY = add.getY();
+                        if (valY instanceof ConstantNode) {
+                            int addBy = valY.asJavaConstant().asInt();
+                            displacement = displacement + scale.value * addBy;
+                            replaceFirstInput(index, phi);
+                            tool.addToWorkList(index);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool gen) {
         LIRGeneratorTool tool = gen.getLIRGeneratorTool();
@@ -135,4 +163,9 @@
     public long getMaxConstantDisplacement() {
         return displacement;
     }
+
+    @Override
+    public void simplify(SimplifierTool tool) {
+        canonicalizeIndex(tool);
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,7 +36,6 @@
 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.SD;
 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.SS;
 
-import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64MIOp;
 import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64RMOp;
 import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64RRMOp;
@@ -44,12 +43,12 @@
 import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize;
 import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp;
 import org.graalvm.compiler.core.common.LIRKind;
+import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.gen.NodeLIRBuilder;
 import org.graalvm.compiler.core.gen.NodeMatchRules;
 import org.graalvm.compiler.core.match.ComplexMatchResult;
 import org.graalvm.compiler.core.match.MatchRule;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIRFrameState;
 import org.graalvm.compiler.lir.LIRValueUtil;
@@ -147,7 +146,7 @@
                 matchedAsConstant = true;
             }
             if (kind.isXMM()) {
-                Debug.log("Skipping constant compares for float kinds");
+                ifNode.getDebug().log("Skipping constant compares for float kinds");
                 return null;
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,8 +24,8 @@
 
 import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
-import org.graalvm.compiler.options.OptionKey;
 
 /**
  * This class encapsulates options that control the behavior of the Graal compiler.
@@ -109,6 +109,9 @@
     @Option(help = "", type = OptionType.Debug)
     public static final OptionKey<Boolean> LoopUnswitch = new OptionKey<>(true);
 
+    @Option(help = "", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PartialUnroll = new OptionKey<>(true);
+
     @Option(help = "", type = OptionType.Expert)
     public static final OptionKey<Float> MinimumPeelProbability = new OptionKey<>(0.35f);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/BiDirectionalTraceBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/BiDirectionalTraceBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,7 +31,7 @@
 
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult.TrivialTracePredicate;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 
 /**
@@ -40,8 +40,8 @@
  */
 public final class BiDirectionalTraceBuilder {
 
-    public static TraceBuilderResult computeTraces(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
-        return new BiDirectionalTraceBuilder(blocks).build(startBlock, blocks, pred);
+    public static TraceBuilderResult computeTraces(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+        return new BiDirectionalTraceBuilder(blocks).build(debug, startBlock, blocks, pred);
     }
 
     private final Deque<AbstractBlockBase<?>> worklist;
@@ -69,22 +69,22 @@
     }
 
     @SuppressWarnings("try")
-    private TraceBuilderResult build(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
-        try (Indent indent = Debug.logAndIndent("BiDirectionalTraceBuilder: start trace building")) {
-            ArrayList<Trace> traces = buildTraces();
+    private TraceBuilderResult build(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+        try (Indent indent = debug.logAndIndent("BiDirectionalTraceBuilder: start trace building")) {
+            ArrayList<Trace> traces = buildTraces(debug);
             assert traces.get(0).getBlocks()[0].equals(startBlock) : "The first traces always contains the start block";
-            return TraceBuilderResult.create(blocks, traces, blockToTrace, pred);
+            return TraceBuilderResult.create(debug, blocks, traces, blockToTrace, pred);
         }
     }
 
-    protected ArrayList<Trace> buildTraces() {
+    protected ArrayList<Trace> buildTraces(DebugContext debug) {
         ArrayList<Trace> traces = new ArrayList<>();
         // process worklist
         while (!worklist.isEmpty()) {
             AbstractBlockBase<?> block = worklist.pollFirst();
             assert block != null;
             if (!processed(block)) {
-                Trace trace = new Trace(startTrace(block));
+                Trace trace = new Trace(startTrace(debug, block));
                 for (AbstractBlockBase<?> traceBlock : trace.getBlocks()) {
                     blockToTrace[traceBlock.getId()] = trace;
                 }
@@ -97,14 +97,16 @@
 
     /**
      * Build a new trace starting at {@code block}.
+     *
+     * @param debug
      */
     @SuppressWarnings("try")
-    private Collection<AbstractBlockBase<?>> startTrace(AbstractBlockBase<?> block) {
+    private Collection<AbstractBlockBase<?>> startTrace(DebugContext debug, AbstractBlockBase<?> block) {
         ArrayDeque<AbstractBlockBase<?>> trace = new ArrayDeque<>();
-        try (Indent i = Debug.logAndIndent("StartTrace: %s", block)) {
-            try (Indent indentFront = Debug.logAndIndent("Head:")) {
+        try (Indent i = debug.logAndIndent("StartTrace: %s", block)) {
+            try (Indent indentFront = debug.logAndIndent("Head:")) {
                 for (AbstractBlockBase<?> currentBlock = block; currentBlock != null; currentBlock = selectPredecessor(currentBlock)) {
-                    addBlockToTrace(currentBlock);
+                    addBlockToTrace(debug, currentBlock);
                     trace.addFirst(currentBlock);
                 }
             }
@@ -114,21 +116,21 @@
                 b.setLinearScanNumber(blockNr++);
             }
 
-            try (Indent indentBack = Debug.logAndIndent("Tail:")) {
+            try (Indent indentBack = debug.logAndIndent("Tail:")) {
                 for (AbstractBlockBase<?> currentBlock = selectSuccessor(block); currentBlock != null; currentBlock = selectSuccessor(currentBlock)) {
-                    addBlockToTrace(currentBlock);
+                    addBlockToTrace(debug, currentBlock);
                     trace.addLast(currentBlock);
                     /* This time we can number the blocks immediately as we go forwards. */
                     currentBlock.setLinearScanNumber(blockNr++);
                 }
             }
         }
-        Debug.log("Trace: %s", trace);
+        debug.log("Trace: %s", trace);
         return trace;
     }
 
-    private void addBlockToTrace(AbstractBlockBase<?> currentBlock) {
-        Debug.log("add %s (prob: %f)", currentBlock, currentBlock.probability());
+    private void addBlockToTrace(DebugContext debug, AbstractBlockBase<?> currentBlock) {
+        debug.log("add %s (prob: %f)", currentBlock, currentBlock.probability());
         processed.set(currentBlock.getId());
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/SingleBlockTraceBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/SingleBlockTraceBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,17 +26,18 @@
 
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult.TrivialTracePredicate;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
+import org.graalvm.compiler.debug.DebugContext;
 
 /**
  * Builds traces consisting of a single basic block.
  */
 public final class SingleBlockTraceBuilder {
 
-    public static TraceBuilderResult computeTraces(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
-        return build(startBlock, blocks, pred);
+    public static TraceBuilderResult computeTraces(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+        return build(debug, startBlock, blocks, pred);
     }
 
-    private static TraceBuilderResult build(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+    private static TraceBuilderResult build(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
         Trace[] blockToTrace = new Trace[blocks.length];
         ArrayList<Trace> traces = new ArrayList<>(blocks.length);
 
@@ -49,7 +50,7 @@
         }
 
         assert traces.get(0).getBlocks()[0].equals(startBlock) : "The first traces always contains the start block";
-        return TraceBuilderResult.create(blocks, traces, blockToTrace, pred);
+        return TraceBuilderResult.create(debug, blocks, traces, blockToTrace, pred);
     }
 
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceBuilderResult.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceBuilderResult.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,7 +27,7 @@
 import java.util.BitSet;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 
 public final class TraceBuilderResult {
@@ -39,9 +39,9 @@
     private final ArrayList<Trace> traces;
     private final Trace[] blockToTrace;
 
-    static TraceBuilderResult create(AbstractBlockBase<?>[] blocks, ArrayList<Trace> traces, Trace[] blockToTrace, TrivialTracePredicate pred) {
+    static TraceBuilderResult create(DebugContext debug, AbstractBlockBase<?>[] blocks, ArrayList<Trace> traces, Trace[] blockToTrace, TrivialTracePredicate pred) {
         connect(traces, blockToTrace);
-        ArrayList<Trace> newTraces = reorderTraces(traces, pred);
+        ArrayList<Trace> newTraces = reorderTraces(debug, traces, pred);
         TraceBuilderResult traceBuilderResult = new TraceBuilderResult(newTraces, blockToTrace);
         traceBuilderResult.numberTraces();
         assert verify(traceBuilderResult, blocks.length);
@@ -157,11 +157,11 @@
     }
 
     @SuppressWarnings("try")
-    private static ArrayList<Trace> reorderTraces(ArrayList<Trace> oldTraces, TrivialTracePredicate pred) {
+    private static ArrayList<Trace> reorderTraces(DebugContext debug, ArrayList<Trace> oldTraces, TrivialTracePredicate pred) {
         if (pred == null) {
             return oldTraces;
         }
-        try (Indent indent = Debug.logAndIndent("ReorderTrace")) {
+        try (Indent indent = debug.logAndIndent("ReorderTrace")) {
             ArrayList<Trace> newTraces = new ArrayList<>(oldTraces.size());
             for (int oldTraceIdx = 0; oldTraceIdx < oldTraces.size(); oldTraceIdx++) {
                 Trace currentTrace = oldTraces.get(oldTraceIdx);
@@ -171,7 +171,7 @@
                     addTrace(newTraces, currentTrace);
                     for (Trace succTrace : currentTrace.getSuccessors()) {
                         if (pred.isTrivialTrace(succTrace) && !alreadyProcessed(newTraces, succTrace)) {
-                            Debug.log("Moving trivial trace from %d to %d", succTrace.getId(), newTraces.size());
+                            debug.log("Moving trivial trace from %d to %d", succTrace.getId(), newTraces.size());
                             // add trivial successor trace
                             addTrace(newTraces, succTrace);
                         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceStatisticsPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/TraceStatisticsPrinter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,33 +25,32 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 
 public final class TraceStatisticsPrinter {
     private static final String SEP = ";";
 
     @SuppressWarnings("try")
-    public static void printTraceStatistics(TraceBuilderResult result, String compilationUnitName) {
-        try (Scope s = Debug.scope("DumpTraceStatistics")) {
-            if (Debug.isLogEnabled(Debug.VERBOSE_LEVEL)) {
-                print(result, compilationUnitName);
+    public static void printTraceStatistics(DebugContext debug, TraceBuilderResult result, String compilationUnitName) {
+        try (DebugContext.Scope s = debug.scope("DumpTraceStatistics")) {
+            if (debug.isLogEnabled(DebugContext.VERBOSE_LEVEL)) {
+                print(debug, result, compilationUnitName);
             }
         } catch (Throwable e) {
-            Debug.handle(e);
+            debug.handle(e);
         }
     }
 
     @SuppressWarnings("try")
-    protected static void print(TraceBuilderResult result, String compilationUnitName) {
+    protected static void print(DebugContext debug, TraceBuilderResult result, String compilationUnitName) {
         List<Trace> traces = result.getTraces();
         int numTraces = traces.size();
 
-        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");
+        try (Indent indent0 = debug.logAndIndent(DebugContext.VERBOSE_LEVEL, "<tracestatistics>")) {
+            debug.log(DebugContext.VERBOSE_LEVEL, "<name>%s</name>", compilationUnitName != null ? compilationUnitName : "null");
+            try (Indent indent1 = debug.logAndIndent(DebugContext.VERBOSE_LEVEL, "<traces>")) {
+                printRawLine(debug, "tracenumber", "total", "min", "max", "numBlocks");
                 for (int i = 0; i < numTraces; i++) {
                     AbstractBlockBase<?>[] t = traces.get(i).getBlocks();
                     double total = 0;
@@ -67,20 +66,20 @@
                             max = probability;
                         }
                     }
-                    printLine(i, total, min, max, t.length);
+                    printLine(debug, i, total, min, max, t.length);
                 }
             }
-            Debug.log(Debug.VERBOSE_LEVEL, "</traces>");
+            debug.log(DebugContext.VERBOSE_LEVEL, "</traces>");
         }
-        Debug.log(Debug.VERBOSE_LEVEL, "</tracestatistics>");
+        debug.log(DebugContext.VERBOSE_LEVEL, "</tracestatistics>");
 
     }
 
-    private static void printRawLine(Object tracenr, Object totalTime, Object minProb, Object maxProb, Object numBlocks) {
-        Debug.log(Debug.VERBOSE_LEVEL, "%s", String.join(SEP, tracenr.toString(), totalTime.toString(), minProb.toString(), maxProb.toString(), numBlocks.toString()));
+    private static void printRawLine(DebugContext debug, Object tracenr, Object totalTime, Object minProb, Object maxProb, Object numBlocks) {
+        debug.log(DebugContext.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) {
-        printRawLine(tracenr, totalTime, minProb, maxProb, numBlocks);
+    private static void printLine(DebugContext debug, int tracenr, double totalTime, double minProb, double maxProb, int numBlocks) {
+        printRawLine(debug, tracenr, totalTime, minProb, maxProb, numBlocks);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/UniDirectionalTraceBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/UniDirectionalTraceBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,7 +29,7 @@
 
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult.TrivialTracePredicate;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 
 /**
@@ -37,8 +37,8 @@
  */
 public final class UniDirectionalTraceBuilder {
 
-    public static TraceBuilderResult computeTraces(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
-        return new UniDirectionalTraceBuilder(blocks).build(startBlock, blocks, pred);
+    public static TraceBuilderResult computeTraces(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+        return new UniDirectionalTraceBuilder(blocks).build(debug, startBlock, blocks, pred);
     }
 
     private final PriorityQueue<AbstractBlockBase<?>> worklist;
@@ -71,14 +71,14 @@
     }
 
     @SuppressWarnings("try")
-    private TraceBuilderResult build(AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
-        try (Indent indent = Debug.logAndIndent("UniDirectionalTraceBuilder: start trace building: %s", startBlock)) {
-            ArrayList<Trace> traces = buildTraces(startBlock);
-            return TraceBuilderResult.create(blocks, traces, blockToTrace, pred);
+    private TraceBuilderResult build(DebugContext debug, AbstractBlockBase<?> startBlock, AbstractBlockBase<?>[] blocks, TrivialTracePredicate pred) {
+        try (Indent indent = debug.logAndIndent("UniDirectionalTraceBuilder: start trace building: %s", startBlock)) {
+            ArrayList<Trace> traces = buildTraces(debug, startBlock);
+            return TraceBuilderResult.create(debug, blocks, traces, blockToTrace, pred);
         }
     }
 
-    protected ArrayList<Trace> buildTraces(AbstractBlockBase<?> startBlock) {
+    protected ArrayList<Trace> buildTraces(DebugContext debug, AbstractBlockBase<?> startBlock) {
         ArrayList<Trace> traces = new ArrayList<>();
         // add start block
         worklist.add(startBlock);
@@ -87,7 +87,7 @@
             AbstractBlockBase<?> block = worklist.poll();
             assert block != null;
             if (!processed(block)) {
-                Trace trace = new Trace(startTrace(block));
+                Trace trace = new Trace(startTrace(debug, block));
                 for (AbstractBlockBase<?> traceBlock : trace.getBlocks()) {
                     blockToTrace[traceBlock.getId()] = trace;
                 }
@@ -102,13 +102,13 @@
      * Build a new trace starting at {@code block}.
      */
     @SuppressWarnings("try")
-    private List<AbstractBlockBase<?>> startTrace(AbstractBlockBase<?> block) {
+    private List<AbstractBlockBase<?>> startTrace(DebugContext debug, AbstractBlockBase<?> block) {
         assert checkPredecessorsProcessed(block);
         ArrayList<AbstractBlockBase<?>> trace = new ArrayList<>();
         int blockNumber = 0;
-        try (Indent i = Debug.logAndIndent("StartTrace: %s", block)) {
+        try (Indent i = debug.logAndIndent("StartTrace: %s", block)) {
             for (AbstractBlockBase<?> currentBlock = block; currentBlock != null; currentBlock = selectNext(currentBlock)) {
-                Debug.log("add %s (prob: %f)", currentBlock, currentBlock.probability());
+                debug.log("add %s (prob: %f)", currentBlock, currentBlock.probability());
                 processed.set(currentBlock.getId());
                 trace.add(currentBlock);
                 unblock(currentBlock);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java	Fri Jul 07 09:40:47 2017 -0700
@@ -87,6 +87,10 @@
         return exits;
     }
 
+    public void addExit(T t) {
+        exits.add(t);
+    }
+
     /**
      * Determines if one loop is a transitive parent of another loop.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,6 @@
  */
 package org.graalvm.compiler.core.test;
 
-import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -48,14 +46,11 @@
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.bytecode.BridgeMethodUtils;
 import org.graalvm.compiler.core.CompilerThreadFactory;
-import org.graalvm.compiler.core.CompilerThreadFactory.DebugConfigAccess;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DebugEnvironment;
-import org.graalvm.compiler.debug.DelegatingDebugConfig;
-import org.graalvm.compiler.debug.GraalDebugConfig;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -203,12 +198,7 @@
         String[] filters = property == null ? null : property.split(",");
 
         OptionValues options = getInitialOptions();
-        CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread", new DebugConfigAccess() {
-            @Override
-            public GraalDebugConfig getDebugConfig() {
-                return DebugEnvironment.ensureInitialized(options);
-            }
-        });
+        CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread");
         int availableProcessors = Runtime.getRuntime().availableProcessors();
         ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory);
 
@@ -216,17 +206,19 @@
 
         for (Method m : BadUsageWithEquals.class.getDeclaredMethods()) {
             ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-            StructuredGraph graph = new StructuredGraph.Builder(options, AllowAssumptions.YES).method(method).build();
-            try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) {
-                graphBuilderSuite.apply(graph, context);
-                // update phi stamps
-                graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
-                checkGraph(context, graph);
-                errors.add(String.format("Expected error while checking %s", m));
-            } catch (VerificationError e) {
-                // expected!
-            } catch (Throwable e) {
-                errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e)));
+            try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) {
+                StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(method).build();
+                try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) {
+                    graphBuilderSuite.apply(graph, context);
+                    // update phi stamps
+                    graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
+                    checkGraph(context, graph);
+                    errors.add(String.format("Expected error while checking %s", m));
+                } catch (VerificationError e) {
+                    // expected!
+                } catch (Throwable e) {
+                    errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e)));
+                }
             }
         }
         if (errors.isEmpty()) {
@@ -251,26 +243,29 @@
                         String methodName = className + "." + m.getName();
                         if (matches(filters, methodName)) {
                             executor.execute(() -> {
-                                ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-                                StructuredGraph graph = new StructuredGraph.Builder(options).method(method).build();
-                                try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) {
-                                    checkMethod(method);
-                                    graphBuilderSuite.apply(graph, context);
-                                    // update phi stamps
-                                    graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
-                                    checkGraph(context, graph);
-                                } catch (VerificationError e) {
-                                    errors.add(e.getMessage());
-                                } catch (LinkageError e) {
-                                    // suppress linkages errors resulting from eager resolution
-                                } catch (BailoutException e) {
-                                    // Graal bail outs on certain patterns in Java bytecode (e.g.,
-                                    // unbalanced monitors introduced by jacoco).
-                                } catch (Throwable e) {
-                                    try {
-                                        tool.handleParsingException(e);
-                                    } catch (Throwable t) {
-                                        errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e)));
+                                try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) {
+                                    ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
+                                    StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
+                                    try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) {
+                                        checkMethod(method);
+                                        graphBuilderSuite.apply(graph, context);
+                                        // update phi stamps
+                                        graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
+                                        checkGraph(context, graph);
+                                    } catch (VerificationError e) {
+                                        errors.add(e.getMessage());
+                                    } catch (LinkageError e) {
+                                        // suppress linkages errors resulting from eager resolution
+                                    } catch (BailoutException e) {
+                                        // Graal bail outs on certain patterns in Java bytecode
+                                        // (e.g.,
+                                        // unbalanced monitors introduced by jacoco).
+                                    } catch (Throwable e) {
+                                        try {
+                                            tool.handleParsingException(e);
+                                        } catch (Throwable t) {
+                                            errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e)));
+                                        }
                                     }
                                 }
                             });
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest13.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest13.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
@@ -310,7 +310,8 @@
         super.prepareGraph(graph, canonicalizer, context, applyLowering);
         graph.clearAllStateAfter();
         graph.setGuardsStage(StructuredGraph.GuardsStage.AFTER_FSA);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "After preparation");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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/ConditionalEliminationTestBase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,16 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.ConditionalEliminationPhase;
 import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase;
 import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
-import org.graalvm.compiler.phases.common.ConditionalEliminationPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 import org.junit.Assert;
@@ -52,21 +52,22 @@
     @SuppressWarnings("try")
     protected void testConditionalElimination(String snippet, String referenceSnippet, boolean applyConditionalEliminationOnReference, boolean applyLowering) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         PhaseContext context = new PhaseContext(getProviders());
         CanonicalizerPhase canonicalizer1 = new CanonicalizerPhase();
         CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        try (Debug.Scope scope = Debug.scope("ConditionalEliminationTest", graph)) {
+        try (DebugContext.Scope scope = debug.scope("ConditionalEliminationTest", graph)) {
             prepareGraph(graph, canonicalizer1, context, applyLowering);
             new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, context);
             canonicalizer.apply(graph, context);
             canonicalizer.apply(graph, context);
             new ConvertDeoptimizeToGuardPhase().apply(graph, context);
         } catch (Throwable t) {
-            Debug.handle(t);
+            debug.handle(t);
         }
         StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.YES);
-        try (Debug.Scope scope = Debug.scope("ConditionalEliminationTest.ReferenceGraph", referenceGraph)) {
+        try (DebugContext.Scope scope = debug.scope("ConditionalEliminationTest.ReferenceGraph", referenceGraph)) {
             prepareGraph(referenceGraph, canonicalizer, context, applyLowering);
             if (applyConditionalEliminationOnReference) {
                 new ConditionalEliminationPhase(true).apply(referenceGraph, context);
@@ -75,7 +76,7 @@
             canonicalizer.apply(referenceGraph, context);
             new ConvertDeoptimizeToGuardPhase().apply(graph, context);
         } catch (Throwable t) {
-            Debug.handle(t);
+            debug.handle(t);
         }
         assertEquals(referenceGraph, graph);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,14 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.junit.Test;
 
 /**
  * In the following tests, the usages of local variable "a" are replaced with the integer constant
@@ -81,17 +79,18 @@
 
     @SuppressWarnings("try")
     private void test(final String snippet) {
-        try (Scope s = Debug.scope("DegeneratedLoopsTest", new DebugDumpScope(snippet))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("DegeneratedLoopsTest", new DebugDumpScope(snippet))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             HighTierContext context = getDefaultHighTierContext();
             new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
             new CanonicalizerPhase().apply(graph, context);
-            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
             StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.YES);
-            Debug.dump(Debug.BASIC_LEVEL, referenceGraph, "ReferenceGraph");
+            debug.dump(DebugContext.BASIC_LEVEL, referenceGraph, "ReferenceGraph");
             assertEquals(referenceGraph, graph);
         } catch (Throwable e) {
-            throw Debug.handle(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/DontReuseArgumentSpaceTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DontReuseArgumentSpaceTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.phases.HighTier;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.options.OptionValues;
@@ -72,7 +73,8 @@
         ResolvedJavaMethod javaMethod = getResolvedJavaMethod("killArguments");
         StructuredGraph graph = parseEager(javaMethod, AllowAssumptions.YES);
         CompilationResult compilationResult = compile(javaMethod, graph);
-        getBackend().createDefaultInstalledCode(javaMethod, compilationResult);
+        DebugContext debug = getDebugContext();
+        getBackend().createDefaultInstalledCode(debug, javaMethod, compilationResult);
 
         test("callTwice", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,26 +28,26 @@
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
 
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.java.GraphBuilderPhase;
+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.java.RegisterFinalizerNode;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.OptimisticOptimizations;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.inlining.InliningPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.junit.Assert;
+import org.junit.Test;
+
 import jdk.vm.ci.meta.Assumptions;
 import jdk.vm.ci.meta.Assumptions.Assumption;
 import jdk.vm.ci.meta.Assumptions.LeafType;
 import jdk.vm.ci.meta.Assumptions.NoFinalizableSubclass;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.java.GraphBuilderPhase;
-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.java.RegisterFinalizerNode;
-import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
-import org.graalvm.compiler.phases.common.inlining.InliningPhase;
-import org.graalvm.compiler.phases.tiers.HighTierContext;
-
 public class FinalizableSubclassTest extends GraalCompilerTest {
 
     /**
@@ -71,7 +71,8 @@
         Constructor<?>[] constructors = cl.getConstructors();
         Assert.assertTrue(constructors.length == 1);
         final ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(constructors[0]);
-        StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions(), allowAssumptions).method(javaMethod).build();
+        OptionValues options = getInitialOptions();
+        StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options), allowAssumptions).method(javaMethod).build();
 
         GraphBuilderConfiguration conf = GraphBuilderConfiguration.getSnippetDefault(getDefaultGraphBuilderPlugins());
         new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getProviders().getConstantReflection(), getProviders().getConstantFieldProvider(), conf,
@@ -107,8 +108,9 @@
      */
     @Test
     public void test1() throws ClassNotFoundException {
+        DebugContext debug = getDebugContext();
         for (int i = 0; i < 2; i++) {
-            ClassTemplateLoader loader = new ClassTemplateLoader();
+            ClassTemplateLoader loader = new ClassTemplateLoader(debug);
             checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), true, AllowAssumptions.NO);
             checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), false, AllowAssumptions.YES);
 
@@ -126,8 +128,11 @@
         private final String replaceTo;
         private HashMap<String, Class<?>> cache = new HashMap<>();
 
-        ClassTemplateLoader() {
+        private final DebugContext debug;
+
+        ClassTemplateLoader(DebugContext debug) {
             loaderInstance++;
+            this.debug = debug;
             replaceTo = String.format("%04d", loaderInstance);
         }
 
@@ -155,14 +160,15 @@
             } catch (IOException e) {
                 Assert.fail("can't access class: " + name);
             }
-            dumpStringsInByteArray(classData);
+
+            dumpStringsInByteArray(debug, classData);
 
             // replace all occurrences of "AAAA" in classfile
             int index = -1;
             while ((index = indexOfAAAA(classData, index + 1)) != -1) {
                 replaceAAAA(classData, index, replaceTo);
             }
-            dumpStringsInByteArray(classData);
+            dumpStringsInByteArray(debug, classData);
 
             Class<?> c = defineClass(null, classData, 0, classData.length);
             cache.put(nameReplaced, c);
@@ -192,14 +198,14 @@
             }
         }
 
-        private static void dumpStringsInByteArray(byte[] b) {
+        private static void dumpStringsInByteArray(DebugContext debug, byte[] b) {
             boolean wasChar = true;
             StringBuilder sb = new StringBuilder();
             for (Byte x : b) {
                 // check for [a-zA-Z0-9]
                 if ((x >= 0x41 && x <= 0x7a) || (x >= 0x30 && x <= 0x39)) {
                     if (!wasChar) {
-                        Debug.log(sb + "");
+                        debug.log(sb + "");
                         sb.setLength(0);
                     }
                     sb.append(String.format("%c", x));
@@ -208,7 +214,7 @@
                     wasChar = false;
                 }
             }
-            Debug.log(sb + "");
+            debug.log(sb + "");
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FindUniqueDefaultMethodTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FindUniqueDefaultMethodTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,19 +22,17 @@
  */
 package org.graalvm.compiler.core.test;
 
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.nodes.ReturnNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.junit.Ignore;
+import org.junit.Test;
+
 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
-import org.junit.Ignore;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
-import org.graalvm.compiler.nodes.ReturnNode;
-import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-
 /**
  * This test illustrates problems and limitations with class hierarchy analysis when default methods
  * are involved.
@@ -139,13 +137,14 @@
 
     @SuppressWarnings("try")
     protected StructuredGraph buildGraph(final String snippet) {
-        try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             compile(graph.method(), graph);
-            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
+            debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ReturnNode;
@@ -39,6 +35,8 @@
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Assert;
+import org.junit.Test;
 
 public class FloatingReadTest extends GraphScheduleTest {
 
@@ -63,7 +61,8 @@
 
     @SuppressWarnings("try")
     private void test(final String snippet) {
-        try (Scope s = Debug.scope("FloatingReadTest", new DebugDumpScope(snippet))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("FloatingReadTest", new DebugDumpScope(snippet))) {
 
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             PhaseContext context = new PhaseContext(getProviders());
@@ -82,7 +81,7 @@
                 }
             }
 
-            Debug.dump(Debug.BASIC_LEVEL, graph, "After lowering");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "After lowering");
 
             Assert.assertNotNull(returnNode);
             Assert.assertNotNull(monitorexit);
@@ -92,7 +91,7 @@
 
             assertOrderedAfterSchedule(graph, read, (Node) monitorexit);
         } catch (Throwable e) {
-            throw Debug.handle(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/GraalCompilerAssumptionsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerAssumptionsTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -65,7 +65,7 @@
         checkGraph(expectedAssumption, graph);
 
         CompilationResult compilationResult = compile(javaMethod, graph);
-        final InstalledCode installedCode = getBackend().createDefaultInstalledCode(javaMethod, compilationResult);
+        final InstalledCode installedCode = getBackend().createDefaultInstalledCode(graph.getDebug(), javaMethod, compilationResult);
         assertTrue(installedCode.isValid());
         if (classToLoad != null) {
             String fullName = getClass().getName() + "$" + classToLoad;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -37,6 +37,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
@@ -55,10 +56,10 @@
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.type.StampFactory;
 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.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpHandler;
 import org.graalvm.compiler.debug.DebugDumpScope;
-import org.graalvm.compiler.debug.DebugEnvironment;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.Node;
@@ -85,6 +86,7 @@
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.cfg.Block;
@@ -111,13 +113,13 @@
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.graalvm.compiler.phases.tiers.TargetProvider;
 import org.graalvm.compiler.phases.util.Providers;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.graalvm.compiler.test.AddExports;
 import org.graalvm.compiler.test.GraalTest;
 import org.graalvm.compiler.test.JLModule;
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.internal.AssumptionViolatedException;
 
@@ -364,14 +366,7 @@
         this.providers = backend.getProviders();
     }
 
-    private Scope debugScope;
-
-    @Before
-    public void beforeTest() {
-        assert debugScope == null;
-        debugScope = Debug.scope(getClass());
-    }
-
+    @Override
     @After
     public void afterTest() {
         if (invocationPluginExtensions != null) {
@@ -383,10 +378,21 @@
                 }
             }
         }
-        if (debugScope != null) {
-            debugScope.close();
-        }
-        debugScope = null;
+        super.afterTest();
+    }
+
+    /**
+     * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if
+     * none currently exists. Debug contexts created by this method will have their
+     * {@link DebugDumpHandler}s closed in {@link #afterTest()}.
+     */
+    protected DebugContext getDebugContext() {
+        return getDebugContext(getInitialOptions());
+    }
+
+    @Override
+    protected Collection<DebugHandlersFactory> getDebugHandlersFactories() {
+        return Collections.singletonList(new GraalDebugHandlersFactory(getSnippetReflection()));
     }
 
     protected void assertEquals(StructuredGraph expected, StructuredGraph graph) {
@@ -413,13 +419,13 @@
         String mismatchString = compareGraphStrings(expected, expectedString, graph, actualString);
 
         if (!excludeVirtual && getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
-            Debug.dump(Debug.BASIC_LEVEL, expected, "Node count not matching - expected");
-            Debug.dump(Debug.BASIC_LEVEL, graph, "Node count not matching - actual");
+            expected.getDebug().dump(DebugContext.BASIC_LEVEL, expected, "Node count not matching - expected");
+            graph.getDebug().dump(DebugContext.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_LEVEL, expected, "mismatching graphs - expected");
-            Debug.dump(Debug.BASIC_LEVEL, graph, "mismatching graphs - actual");
+            expected.getDebug().dump(DebugContext.BASIC_LEVEL, expected, "mismatching graphs - expected");
+            graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "mismatching graphs - actual");
             Assert.fail(mismatchString);
         }
     }
@@ -932,24 +938,27 @@
             final CompilationIdentifier id = getOrCreateCompilationId(installedCodeOwner, graph);
 
             InstalledCode installedCode = null;
-            try (AllocSpy spy = AllocSpy.open(installedCodeOwner); Scope ds = Debug.scope("Compiling", new DebugDumpScope(id.toString(CompilationIdentifier.Verbosity.ID), true))) {
+            StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner, id, options) : graph;
+            DebugContext debug = graphToCompile.getDebug();
+            try (AllocSpy spy = AllocSpy.open(installedCodeOwner); DebugContext.Scope ds = debug.scope("Compiling", new DebugDumpScope(id.toString(CompilationIdentifier.Verbosity.ID), true))) {
                 final boolean printCompilation = PrintCompilation.getValue(options) && !TTY.isSuppressed();
                 if (printCompilation) {
                     TTY.println(String.format("@%-6s Graal %-70s %-45s %-50s ...", id, installedCodeOwner.getDeclaringClass().getName(), installedCodeOwner.getName(),
                                     installedCodeOwner.getSignature()));
                 }
                 long start = System.currentTimeMillis();
-                CompilationResult compResult = compile(installedCodeOwner, graph, new CompilationResult(), id, options);
+                CompilationResult compResult = compile(installedCodeOwner, graphToCompile, new CompilationResult(), id, options);
                 if (printCompilation) {
                     TTY.println(String.format("@%-6s Graal %-70s %-45s %-50s | %4dms %5dB", id, "", "", "", System.currentTimeMillis() - start, compResult.getTargetCodeSize()));
                 }
 
-                try (Scope s = Debug.scope("CodeInstall", getCodeCache(), installedCodeOwner, compResult)) {
+                try (DebugContext.Scope s = debug.scope("CodeInstall", getCodeCache(), installedCodeOwner, compResult);
+                                DebugContext.Activation a = debug.activate()) {
                     try {
                         if (installAsDefault) {
-                            installedCode = addDefaultMethod(installedCodeOwner, compResult);
+                            installedCode = addDefaultMethod(debug, installedCodeOwner, compResult);
                         } else {
-                            installedCode = addMethod(installedCodeOwner, compResult);
+                            installedCode = addMethod(debug, installedCodeOwner, compResult);
                         }
                         if (installedCode == null) {
                             throw new GraalError("Could not install code for " + installedCodeOwner.format("%H.%n(%p)"));
@@ -963,10 +972,10 @@
                         throw e;
                     }
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
 
             if (!forceCompile) {
@@ -988,6 +997,10 @@
         return parseEager(method, AllowAssumptions.YES, getCompilationId(method), options);
     }
 
+    protected final StructuredGraph parseForCompile(ResolvedJavaMethod method, DebugContext debug) {
+        return parseEager(method, AllowAssumptions.YES, debug);
+    }
+
     protected final StructuredGraph parseForCompile(ResolvedJavaMethod method) {
         return parseEager(method, AllowAssumptions.YES, getCompilationId(method), getInitialOptions());
     }
@@ -1032,13 +1045,14 @@
     protected CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, CompilationResult compilationResult, CompilationIdentifier compilationId, OptionValues options) {
         StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner, compilationId, options) : graph;
         lastCompiledGraph = graphToCompile;
-        try (Scope s = Debug.scope("Compile", graphToCompile)) {
+        DebugContext debug = graphToCompile.getDebug();
+        try (DebugContext.Scope s = debug.scope("Compile", graphToCompile)) {
             assert options != null;
             Request<CompilationResult> request = new Request<>(graphToCompile, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL,
                             graphToCompile.getProfilingInfo(), createSuites(options), createLIRSuites(options), compilationResult, CompilationResultBuilderFactory.Default);
             return GraalCompiler.compile(request);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -1048,12 +1062,12 @@
         return null;
     }
 
-    protected InstalledCode addMethod(final ResolvedJavaMethod method, final CompilationResult compilationResult) {
-        return backend.addInstalledCode(method, null, compilationResult);
+    protected InstalledCode addMethod(DebugContext debug, final ResolvedJavaMethod method, final CompilationResult compilationResult) {
+        return backend.addInstalledCode(debug, method, null, compilationResult);
     }
 
-    protected InstalledCode addDefaultMethod(final ResolvedJavaMethod method, final CompilationResult compilationResult) {
-        return backend.createDefaultInstalledCode(method, compilationResult);
+    protected InstalledCode addDefaultMethod(DebugContext debug, final ResolvedJavaMethod method, final CompilationResult compilationResult) {
+        return backend.createDefaultInstalledCode(debug, method, compilationResult);
     }
 
     private final Map<ResolvedJavaMethod, Executable> methodMap = new HashMap<>();
@@ -1108,7 +1122,8 @@
      * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph
      */
     protected final StructuredGraph parseProfiled(String methodName, AllowAssumptions allowAssumptions) {
-        return parseProfiled(getResolvedJavaMethod(methodName), allowAssumptions);
+        ResolvedJavaMethod method = getResolvedJavaMethod(methodName);
+        return parse(builder(method, allowAssumptions), getDefaultGraphBuilderSuite());
     }
 
     /**
@@ -1119,7 +1134,7 @@
      * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph
      */
     protected final StructuredGraph parseProfiled(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) {
-        return parse1(method, getDefaultGraphBuilderSuite(), allowAssumptions, getCompilationId(method), getInitialOptions());
+        return parse(builder(method, allowAssumptions), getDefaultGraphBuilderSuite());
     }
 
     /**
@@ -1130,7 +1145,8 @@
      * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph
      */
     protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions) {
-        return parseEager(getResolvedJavaMethod(methodName), allowAssumptions, getInitialOptions());
+        ResolvedJavaMethod method = getResolvedJavaMethod(methodName);
+        return parse(builder(method, allowAssumptions), getEagerGraphBuilderSuite());
     }
 
     /**
@@ -1142,7 +1158,13 @@
      * @param options the option values to be used when compiling the graph
      */
     protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions, OptionValues options) {
-        return parseEager(getResolvedJavaMethod(methodName), allowAssumptions, options);
+        ResolvedJavaMethod method = getResolvedJavaMethod(methodName);
+        return parse(builder(method, allowAssumptions, options), getEagerGraphBuilderSuite());
+    }
+
+    protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions, DebugContext debug) {
+        ResolvedJavaMethod method = getResolvedJavaMethod(methodName);
+        return parse(builder(method, allowAssumptions, debug), getEagerGraphBuilderSuite());
     }
 
     /**
@@ -1153,7 +1175,11 @@
      * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph
      */
     protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) {
-        return parseEager(method, allowAssumptions, getCompilationId(method), getInitialOptions());
+        return parse(builder(method, allowAssumptions), getEagerGraphBuilderSuite());
+    }
+
+    protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, DebugContext debug) {
+        return parse(builder(method, allowAssumptions, debug), getEagerGraphBuilderSuite());
     }
 
     /**
@@ -1165,7 +1191,7 @@
      * @param options the option values to be used when compiling the graph
      */
     protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, OptionValues options) {
-        return parseEager(method, allowAssumptions, getCompilationId(method), options);
+        return parse(builder(method, allowAssumptions, options), getEagerGraphBuilderSuite());
     }
 
     /**
@@ -1177,43 +1203,53 @@
      * @param compilationId the compilation identifier to be associated with the graph
      * @param options the option values to be used when compiling the graph
      */
-    protected StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        return parse1(method, getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withEagerResolving(true)), allowAssumptions, compilationId, options);
+    protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
+        return parse(builder(method, allowAssumptions, compilationId, options), getEagerGraphBuilderSuite());
+    }
+
+    protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, DebugContext debug) {
+        OptionValues options = debug.getOptions();
+        return new Builder(options, debug, allowAssumptions).method(method).compilationId(getCompilationId(method));
     }
 
-    /**
-     * Parses a Java method using {@linkplain GraphBuilderConfiguration#withFullInfopoints(boolean)
-     * full debug} set to true to produce a graph.
-     *
-     * @param method the method to be parsed
-     * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph
-     */
-    protected StructuredGraph parseDebug(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) {
-        return parse1(method, getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true)), allowAssumptions, getCompilationId(method),
-                        getInitialOptions());
+    protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) {
+        OptionValues options = getInitialOptions();
+        return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method));
+    }
+
+    protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
+        return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(compilationId);
+    }
+
+    protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, OptionValues options) {
+        return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method));
+    }
+
+    protected PhaseSuite<HighTierContext> getDebugGraphBuilderSuite() {
+        return getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true));
     }
 
     @SuppressWarnings("try")
-    private StructuredGraph parse1(ResolvedJavaMethod javaMethod, PhaseSuite<HighTierContext> graphBuilderSuite, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId,
-                    OptionValues options) {
+    protected StructuredGraph parse(StructuredGraph.Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        ResolvedJavaMethod javaMethod = builder.getMethod();
+        if (builder.getCancellable() == null) {
+            builder.cancellable(getCancellable(javaMethod));
+        }
         assert javaMethod.getAnnotation(Test.class) == null : "shouldn't parse method with @Test annotation: " + javaMethod;
-        // @formatter:off
-        StructuredGraph graph = new StructuredGraph.Builder(options, allowAssumptions).
-                        method(javaMethod).
-                        speculationLog(getSpeculationLog()).
-                        useProfilingInfo(true).
-                        compilationId(compilationId).
-                        cancellable(getCancellable(javaMethod)).
-                        build();
-        // @formatter:on
-        try (Scope ds = Debug.scope("Parsing", javaMethod, graph)) {
+        StructuredGraph graph = builder.build();
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope ds = debug.scope("Parsing", javaMethod, graph)) {
             graphBuilderSuite.apply(graph, getDefaultHighTierContext());
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
+    protected PhaseSuite<HighTierContext> getEagerGraphBuilderSuite() {
+        return getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withEagerResolving(true));
+    }
+
     /**
      * Gets the cancellable that should be associated with a graph being created by any of the
      * {@code parse...()} methods.
@@ -1385,6 +1421,6 @@
     public static void initializeForTimeout() {
         // timeout tests run in a separate thread which needs the DebugEnvironment to be
         // initialized
-        DebugEnvironment.ensureInitialized(getInitialOptions());
+        // DebugEnvironment.ensureInitialized(getInitialOptions());
     }
 }
--- /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/GraalDebugHandlersFactoryTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,71 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
+import org.graalvm.compiler.test.AddExports;
+import org.junit.Test;
+
+@AddExports("jdk.internal.vm.compiler/org.graalvm.compiler.printer")
+public class GraalDebugHandlersFactoryTest extends GraalCompilerTest {
+
+    @Test
+    public void createUniqueTest() throws Exception {
+        Field maxFileNameLengthField = GraalDebugHandlersFactory.class.getDeclaredField("MAX_FILE_NAME_LENGTH");
+        maxFileNameLengthField.setAccessible(true);
+        int maxFileNameLength = maxFileNameLengthField.getInt(null);
+        Method createUniqueMethod = GraalDebugHandlersFactory.class.getDeclaredMethod("createUnique", Path.class, String.class, String.class, String.class, boolean.class);
+        createUniqueMethod.setAccessible(true);
+        Path tmpDir = Files.createTempDirectory(Paths.get("."), "createUniqueTest");
+        try {
+            for (boolean createDirectory : new boolean[]{true, false}) {
+                for (String ext : new String[]{"", ".bgv", ".graph-strings"}) {
+                    for (int i = 0; i < maxFileNameLength + 5; i++) {
+                        String id = new String(new char[i]).replace('\0', 'i');
+                        String label = "";
+                        createUniqueMethod.invoke(null, tmpDir, id, label, ext, createDirectory);
+
+                        id = "";
+                        label = new String(new char[i]).replace('\0', 'l');
+                        createUniqueMethod.invoke(null, tmpDir, id, label, ext, createDirectory);
+                    }
+                }
+            }
+        } finally {
+            deleteTree(tmpDir);
+        }
+    }
+
+    private static void deleteTree(Path root) throws IOException {
+        Files.walk(root).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -112,7 +112,7 @@
     @Override
     protected StructuredGraph parseForCompile(ResolvedJavaMethod method, CompilationIdentifier compilationId, OptionValues options) {
         graph = super.parseForCompile(method, compilationId, options);
-        parsedForCompile = (StructuredGraph) graph.copy();
+        parsedForCompile = (StructuredGraph) graph.copy(graph.getDebug());
         return graph;
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,9 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FrameState;
@@ -40,6 +38,7 @@
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 /**
  * In the following tests, the usages of local variable "a" are replaced with the integer constant
@@ -237,6 +236,7 @@
 
     private void test(String snippet) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = graph.getDebug();
         ParameterNode param = graph.getNodes(ParameterNode.TYPE).iterator().next();
         ConstantNode constant = ConstantNode.forInt(0, graph);
         for (Node n : param.usages().snapshot()) {
@@ -244,7 +244,7 @@
                 n.replaceFirstInput(param, constant);
             }
         }
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        debug.dump(DebugContext.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/ImplicitNullCheckTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ImplicitNullCheckTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,8 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
 import org.graalvm.compiler.api.directives.GraalDirectives;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -42,6 +37,9 @@
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
 
 /**
  * Tests that the hub access and the null check are folded.
@@ -68,8 +66,9 @@
 
     @SuppressWarnings("try")
     private void test(final String snippet) {
-        try (Scope s = Debug.scope("FloatingReadTest", new DebugDumpScope(snippet))) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("FloatingReadTest", new DebugDumpScope(snippet))) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             PhaseContext context = new PhaseContext(getProviders());
             new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new FloatingReadPhase().apply(graph);
@@ -80,7 +79,7 @@
             Assert.assertTrue(graph.getNodes().filter(ReadNode.class).first().canNullCheck());
 
         } catch (Throwable e) {
-            throw Debug.handle(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/InfopointReasonTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InfopointReasonTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -76,7 +76,7 @@
     @Test
     public void lineInfopoints() {
         final ResolvedJavaMethod method = getResolvedJavaMethod("testMethod");
-        final StructuredGraph graph = parseDebug(method, AllowAssumptions.ifTrue(OptAssumptions.getValue(getInitialOptions())));
+        final StructuredGraph graph = parse(builder(method, AllowAssumptions.ifTrue(OptAssumptions.getValue(getInitialOptions()))), getDebugGraphBuilderSuite());
         int graphLineSPs = 0;
         for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) {
             if (ipn.getReason() == InfopointReason.BYTECODE_POSITION) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InterfaceMethodHandleTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InterfaceMethodHandleTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import java.lang.invoke.MethodType;
 
 import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.test.ExportingClassLoader;
 import org.junit.Test;
 import org.objectweb.asm.ClassWriter;
@@ -104,12 +105,12 @@
     }
 
     @Override
-    protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult) {
+    protected InstalledCode addMethod(DebugContext debug, ResolvedJavaMethod method, CompilationResult compResult) {
         if (method.getDeclaringClass().equals(getMetaAccess().lookupJavaType(M2Thrower.class))) {
             // Make sure M2Thrower.m2 is invoked from normal code
-            return getBackend().createDefaultInstalledCode(method, compResult);
+            return getBackend().createDefaultInstalledCode(debug, method, compResult);
         }
-        return super.addMethod(method, compResult);
+        return super.addMethod(debug, method, compResult);
     }
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,13 +26,15 @@
 
 import org.junit.Assert;
 import org.junit.Test;
-
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.debug.OpaqueNode;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
@@ -52,7 +54,8 @@
 
     private void longAddChain(boolean reverse) {
         HighTierContext context = getDefaultHighTierContext();
-        StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).build();
+        OptionValues options = getInitialOptions();
+        StructuredGraph graph = new StructuredGraph.Builder(options, DebugContext.create(options, DebugHandlersFactory.LOADER)).build();
         ValueNode constant = graph.unique(ConstantNode.forPrimitive(JavaConstant.INT_1));
         ValueNode value = null;
         if (reverse) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopFullUnrollTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopFullUnrollTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.loop.DefaultLoopPolicies;
 import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
@@ -83,15 +82,16 @@
 
     @SuppressWarnings("try")
     private void test(String snippet, int loopCount) {
-        try (Scope s = Debug.scope(getClass().getSimpleName(), new DebugDumpScope(snippet))) {
-            final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope(getClass().getSimpleName(), new DebugDumpScope(snippet))) {
+            final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
 
             PhaseContext context = new PhaseContext(getProviders());
             new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
 
             assertTrue(graph.getNodes().filter(LoopBeginNode.class).count() == loopCount);
         } catch (Throwable e) {
-            throw Debug.handle(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/LoopUnswitchTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopUnswitchTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,10 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.loop.DefaultLoopPolicies;
 import org.graalvm.compiler.loop.phases.LoopUnswitchingPhase;
@@ -33,6 +30,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 public class LoopUnswitchTest extends GraalCompilerTest {
 
@@ -124,6 +122,7 @@
 
     @SuppressWarnings("try")
     private void test(String snippet, String referenceSnippet) {
+        DebugContext debug = getDebugContext();
         final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         final StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.NO);
 
@@ -135,10 +134,10 @@
 
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         new CanonicalizerPhase().apply(referenceGraph, new PhaseContext(getProviders()));
-        try (Scope s = Debug.scope("Test", new DebugDumpScope("Test:" + snippet))) {
+        try (DebugContext.Scope s = debug.scope("Test", new DebugDumpScope("Test:" + snippet))) {
             assertEquals(referenceGraph, graph);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 }
--- /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/MemoryGraphCanonicalizeTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,84 @@
+/*
+ * 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.memory.WriteNode;
+import org.graalvm.compiler.nodes.spi.LoweringTool;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.FloatingReadPhase;
+import org.graalvm.compiler.phases.common.IncrementalCanonicalizerPhase;
+import org.graalvm.compiler.phases.common.LoweringPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.junit.Test;
+
+public class MemoryGraphCanonicalizeTest extends GraalCompilerTest {
+    static class TestObject {
+        Object object;
+        Integer integer;
+        int value;
+        volatile boolean written;
+    }
+
+    public static void simpleElimination(TestObject object) {
+        object.object = object;
+        object.value = object.integer;
+        object.value = object.integer + 2;
+        object.value = object.integer + 3;
+    }
+
+    @Test
+    public void testSimpleElimination() {
+        testGraph("simpleElimination", 2);
+    }
+
+    public static void complexElimination(TestObject object) {
+        object.object = object;
+        object.value = object.integer;
+        object.value = object.integer + 2;
+        if (object.object == null) {
+            object.value = object.integer + 3;
+        } else {
+            object.object = new Object();
+        }
+        object.written = true;
+        object.value = 5;
+    }
+
+    @Test
+    public void testComplexElimination() {
+        testGraph("complexElimination", 6);
+    }
+
+    public void testGraph(String name, int expectedWrites) {
+        StructuredGraph graph = parseEager(name, StructuredGraph.AllowAssumptions.YES);
+        HighTierContext context = getDefaultHighTierContext();
+        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new IncrementalCanonicalizerPhase<>(canonicalizer, new FloatingReadPhase()).apply(graph, context);
+        new CanonicalizerPhase().apply(graph, context);
+        int writes = graph.getNodes().filter(WriteNode.class).count();
+        assertTrue(writes == expectedWrites, "Expected %d writes, found %d", expectedWrites, writes);
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,12 +31,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.junit.Assert;
-import org.junit.Test;
-
 import org.graalvm.compiler.api.directives.GraalDirectives;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.ReturnNode;
@@ -60,6 +56,8 @@
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
+import org.junit.Assert;
+import org.junit.Test;
 
 /**
  * In these test the FrameStates are explicitly cleared out, so that the scheduling of
@@ -706,7 +704,8 @@
     private ScheduleResult getFinalSchedule(final String snippet, final TestMode mode, final SchedulingStrategy schedulingStrategy) {
         OptionValues options = new OptionValues(getInitialOptions(), OptScheduleOutOfLoops, schedulingStrategy == SchedulingStrategy.LATEST_OUT_OF_LOOPS, OptImplicitNullChecks, false);
         final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, options);
-        try (Scope d = Debug.scope("FloatingReadTest", graph)) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope d = debug.scope("FloatingReadTest", graph)) {
             HighTierContext context = getDefaultHighTierContext();
             CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
             canonicalizer.apply(graph, context);
@@ -717,7 +716,7 @@
             if (mode == TestMode.WITHOUT_FRAMESTATES || mode == TestMode.INLINED_WITHOUT_FRAMESTATES) {
                 graph.clearAllStateAfter();
             }
-            Debug.dump(Debug.BASIC_LEVEL, graph, "after removal of framestates");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "after removal of framestates");
 
             new FloatingReadPhase().apply(graph);
             new RemoveValueProxyPhase().apply(graph);
@@ -732,7 +731,7 @@
             assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count());
             return graph.getLastSchedule();
         } catch (Throwable e) {
-            throw Debug.handle(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/MergeCanonicalizerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,14 +22,13 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 public class MergeCanonicalizerTest extends GraalCompilerTest {
 
@@ -61,7 +60,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_LEVEL, graph, "Graph");
+        graph.getDebug().dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NestedLoopTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,8 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -34,6 +31,8 @@
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
+import org.junit.Assert;
+import org.junit.Test;
 
 public class NestedLoopTest extends GraalCompilerTest {
 
@@ -142,7 +141,8 @@
 
     private void test(String snippet, int rootExits, int nestedExits, int innerExits) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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_LEVEL, graph, "Graph");
+        debug.dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,7 +23,6 @@
 package org.graalvm.compiler.core.test;
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
@@ -196,7 +195,7 @@
         GraphCostPhase gc2 = new GraphCostPhase();
         gc1.apply(g1, htc);
         gc2.apply(g2, htc);
-        Debug.log("Test testDifferentLoopFaster --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
+        g1.getDebug().log("Test testDifferentLoopFaster --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
         Assert.assertTrue(gc2.finalCycles > gc1.finalCycles);
         Assert.assertTrue(gc2.finalSize == gc1.finalSize);
     }
@@ -214,7 +213,7 @@
         GraphCostPhase gc2 = new GraphCostPhase();
         gc1.apply(g1, htc);
         gc2.apply(g2, htc);
-        Debug.log("Test testSameLoopMoreIterationsCostlier --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
+        g1.getDebug().log("Test testSameLoopMoreIterationsCostlier --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
         Assert.assertTrue(gc2.finalCycles > gc1.finalCycles);
         Assert.assertTrue(gc2.finalSize == gc1.finalSize);
     }
@@ -231,7 +230,7 @@
         GraphCostPhase gc2 = new GraphCostPhase();
         gc1.apply(g1, htc);
         gc2.apply(g2, htc);
-        Debug.log("Test testDifferentLoopsInnerOuter --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
+        g1.getDebug().log("Test testDifferentLoopsInnerOuter --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize);
         Assert.assertTrue(gc2.finalSize > gc1.finalSize);
     }
 
@@ -246,7 +245,7 @@
         GraphCostPhase gc2 = new GraphCostPhase();
         gc1.apply(g1, htc);
         gc2.apply(g2, htc);
-        Debug.log("Test Graph Cost --> 1.Graph cost:%f vs. 2.Graph cost:%f\n", gc1.finalCycles, gc2.finalCycles);
+        g1.getDebug().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);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PhiCreationTests.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PhiCreationTests.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,12 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.ValuePhiNode;
+import org.junit.Assert;
+import org.junit.Test;
 
 /**
  * In the following tests, the correct removal of redundant phis during graph building is tested.
@@ -70,7 +69,8 @@
     @Test
     public void test3() {
         StructuredGraph graph = parseEager("test3Snippet", AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         Assert.assertFalse(graph.getNodes().filter(ValuePhiNode.class).iterator().hasNext());
     }
 
@@ -86,7 +86,8 @@
     @Test
     public void test4() {
         StructuredGraph graph = parseEager("test4Snippet", AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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/PushNodesThroughPiTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushNodesThroughPiTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,15 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import jdk.vm.ci.meta.ResolvedJavaField;
-import jdk.vm.ci.meta.ResolvedJavaType;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
@@ -44,6 +36,12 @@
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaType;
 
 public class PushNodesThroughPiTest extends GraalCompilerTest {
 
@@ -77,7 +75,8 @@
     @SuppressWarnings("try")
     public void test1() {
         final String snippet = "test1Snippet";
-        try (Scope s = Debug.scope("PushThroughPi", new DebugDumpScope(snippet))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("PushThroughPi", new DebugDumpScope(snippet))) {
             StructuredGraph graph = compileTestSnippet(snippet);
             for (ReadNode rn : graph.getNodes().filter(ReadNode.class)) {
                 OffsetAddressNode address = (OffsetAddressNode) rn.getAddress();
@@ -96,7 +95,7 @@
 
             Assert.assertTrue(graph.getNodes().filter(IsNullNode.class).count() == 1);
         } catch (Throwable e) {
-            throw Debug.handle(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/PushThroughIfTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,15 +22,14 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 public class PushThroughIfTest extends GraalCompilerTest {
 
@@ -59,7 +58,8 @@
 
     private void test(String snippet, String reference) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -37,6 +33,8 @@
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Assert;
+import org.junit.Test;
 
 /* consider
  *     B b = (B) a;
@@ -84,7 +82,8 @@
 
     @SuppressWarnings("try")
     private void test(final String snippet) {
-        try (Scope s = Debug.scope("ReadAfterCheckCastTest", new DebugDumpScope(snippet))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("ReadAfterCheckCastTest", new DebugDumpScope(snippet))) {
             // check shape of graph, with lots of assumptions. will probably fail if graph
             // structure changes significantly
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
@@ -94,7 +93,7 @@
             new FloatingReadPhase().apply(graph);
             canonicalizer.apply(graph, context);
 
-            Debug.dump(Debug.BASIC_LEVEL, graph, "After lowering");
+            debug.dump(DebugContext.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
@@ -102,7 +101,7 @@
                 Assert.assertTrue(node.getLocationIdentity().isImmutable());
             }
         } catch (Throwable e) {
-            throw Debug.handle(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/ScalarTypeSystemTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,12 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 /**
  * In the following tests, the scalar type system of the compiler should be complete enough to see
@@ -131,7 +130,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_LEVEL, graph, "Graph");
+        graph.getDebug().dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,10 +24,8 @@
 
 import java.util.List;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.nodes.BeginNode;
@@ -51,6 +49,7 @@
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 public class SchedulingTest2 extends GraphScheduleTest {
 
@@ -65,11 +64,12 @@
     @Test
     public void testValueProxyInputs() {
         StructuredGraph graph = parseEager("testSnippet", AllowAssumptions.YES);
+        DebugContext debug = graph.getDebug();
         ReturnNode returnNode = graph.getNodes(ReturnNode.TYPE).first();
         BeginNode beginNode = graph.add(new BeginNode());
         returnNode.replaceAtPredecessor(beginNode);
         beginNode.setNext(returnNode);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        debug.dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SimpleCFGTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,10 +22,7 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.BeginNode;
@@ -37,16 +34,23 @@
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
+import org.junit.Assert;
+import org.junit.Test;
 
 public class SimpleCFGTest extends GraalCompilerTest {
 
     private static void dumpGraph(final StructuredGraph graph) {
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
     }
 
     @Test
     public void testImplies() {
-        StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).build();
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(getSnippetReflection()));
+        StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).build();
 
         EndNode trueEnd = graph.add(new EndNode());
         EndNode falseEnd = graph.add(new EndNode());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StaticInterfaceFieldTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StaticInterfaceFieldTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,19 +23,19 @@
 package org.graalvm.compiler.core.test;
 
 import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
-import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT;
 
 import java.lang.reflect.Method;
 
 import org.graalvm.compiler.api.test.Graal;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DelegatingDebugConfig;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.java.GraphBuilderPhase;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.VerifyPhase;
@@ -90,11 +90,13 @@
 
         final Method m = getMethod(clazz, methodName);
         ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-        StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
-        try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("GraphBuilding", graph, method)) {
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+        StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
+        try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("GraphBuilding", graph, method)) {
             graphBuilderSuite.apply(graph, context);
         } catch (Throwable e) {
-            throw Debug.handle(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/StraighteningTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,12 @@
  */
 package org.graalvm.compiler.core.test;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 public class StraighteningTest extends GraalCompilerTest {
 
@@ -88,7 +87,8 @@
     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_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,7 +28,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodeinfo.Verbosity;
@@ -179,7 +179,8 @@
 
     private void test(String snippet, String referenceSnippet) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         /*
          * When using FlowSensitiveReductionPhase instead of ConditionalEliminationPhase,
          * tail-duplication gets activated thus resulting in a graph with more nodes than the
@@ -198,9 +199,10 @@
 
     @Override
     protected void assertEquals(StructuredGraph expected, StructuredGraph graph) {
+        DebugContext debug = graph.getDebug();
         if (getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
-            Debug.dump(Debug.BASIC_LEVEL, expected, "expected (node count)");
-            Debug.dump(Debug.BASIC_LEVEL, graph, "graph (node count)");
+            debug.dump(DebugContext.BASIC_LEVEL, expected, "expected (node count)");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "graph (node count)");
             Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount());
         }
     }
@@ -243,7 +245,8 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
         new CanonicalizerPhase().apply(graph, new PhaseContext(getProviders()));
-        Debug.dump(Debug.BASIC_LEVEL, graph, "Graph " + snippet);
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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/UnbalancedMonitorsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnbalancedMonitorsTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.junit.Test;
 import org.objectweb.asm.ClassWriter;
@@ -85,7 +86,8 @@
     private void checkForBailout(String name) throws ClassNotFoundException {
         ResolvedJavaMethod method = getResolvedJavaMethod(LOADER.findClass(INNER_CLASS_NAME), name);
         try {
-            StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
+            OptionValues options = getInitialOptions();
+            StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options)).method(method).build();
             Plugins plugins = new Plugins(new InvocationPlugins());
             GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
             OptimisticOptimizations optimisticOpts = OptimisticOptimizations.NONE;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyBailoutUsageTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyBailoutUsageTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,19 +27,19 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.core.common.PermanentBailoutException;
 import org.graalvm.compiler.core.common.RetryableBailoutException;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.java.GraphBuilderPhase;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.Phase;
 import org.graalvm.compiler.phases.PhaseSuite;
@@ -48,6 +48,7 @@
 import org.graalvm.compiler.phases.util.Providers;
 import org.graalvm.compiler.phases.verify.VerifyBailoutUsage;
 import org.graalvm.compiler.runtime.RuntimeProvider;
+import org.junit.Test;
 
 import jdk.vm.ci.code.BailoutException;
 import jdk.vm.ci.meta.MetaAccessProvider;
@@ -125,12 +126,14 @@
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
         graphBuilderSuite.appendPhase(new GraphBuilderPhase(config));
         HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE);
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
         for (Method m : c.getDeclaredMethods()) {
             if (!Modifier.isNative(m.getModifiers()) && !Modifier.isAbstract(m.getModifiers())) {
                 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-                StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
+                StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
                 graphBuilderSuite.apply(graph, context);
-                try (DebugConfigScope s = Debug.disableIntercept()) {
+                try (DebugCloseable s = debug.disableIntercept()) {
                     new VerifyBailoutUsage().apply(graph, context);
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyDebugUsageTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyDebugUsageTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,11 +27,10 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.api.test.Graal;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.graph.Node;
@@ -40,6 +39,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.Phase;
 import org.graalvm.compiler.phases.PhaseSuite;
@@ -48,6 +48,7 @@
 import org.graalvm.compiler.phases.util.Providers;
 import org.graalvm.compiler.phases.verify.VerifyDebugUsage;
 import org.graalvm.compiler.runtime.RuntimeProvider;
+import org.junit.Test;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -58,8 +59,9 @@
 
         @Override
         protected void run(StructuredGraph graph) {
+            DebugContext debug = graph.getDebug();
             for (Node n : graph.getNodes()) {
-                Debug.log("%s", n.toString());
+                debug.log("%s", n.toString());
             }
         }
 
@@ -70,9 +72,10 @@
         @Override
         @SuppressWarnings("try")
         protected void run(StructuredGraph graph) {
-            try (Indent i = Debug.logAndIndent("%s", graph.toString())) {
+            DebugContext debug = graph.getDebug();
+            try (Indent i = debug.logAndIndent("%s", graph.toString())) {
                 for (Node n : graph.getNodes()) {
-                    Debug.log("%s", n);
+                    debug.log("%s", n);
                 }
             }
         }
@@ -83,7 +86,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LEVEL, graph, "%s", graph.toString());
+            DebugContext debug = graph.getDebug();
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "%s", graph.toString());
         }
     }
 
@@ -91,7 +95,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.VERY_DETAILED_LEVEL + 1, graph, "%s", graph);
+            DebugContext debug = graph.getDebug();
+            debug.dump(DebugContext.VERY_DETAILED_LEVEL + 1, graph, "%s", graph);
         }
     }
 
@@ -99,7 +104,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(getLevel(), graph, "%s", graph);
+            DebugContext debug = graph.getDebug();
+            debug.dump(getLevel(), graph, "%s", graph);
         }
 
         int getLevel() {
@@ -111,7 +117,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.verify(graph, "%s", graph.toString());
+            DebugContext debug = graph.getDebug();
+            debug.verify(graph, "%s", graph.toString());
         }
 
     }
@@ -120,8 +127,9 @@
 
         @Override
         protected void run(StructuredGraph graph) {
+            DebugContext debug = graph.getDebug();
             for (Node n : graph.getNodes()) {
-                Debug.log("error " + n);
+                debug.log("error " + n);
             }
         }
 
@@ -132,9 +140,10 @@
         @Override
         @SuppressWarnings("try")
         protected void run(StructuredGraph graph) {
-            try (Indent i = Debug.logAndIndent("error " + graph)) {
+            DebugContext debug = graph.getDebug();
+            try (Indent i = debug.logAndIndent("error " + graph)) {
                 for (Node n : graph.getNodes()) {
-                    Debug.log("%s", n);
+                    debug.log("%s", n);
                 }
             }
         }
@@ -145,7 +154,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LEVEL, graph, "error " + graph);
+            DebugContext debug = graph.getDebug();
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "error " + graph);
         }
 
     }
@@ -154,7 +164,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.verify(graph, "error " + graph);
+            DebugContext debug = graph.getDebug();
+            debug.verify(graph, "error " + graph);
         }
 
     }
@@ -163,8 +174,9 @@
 
         @Override
         protected void run(StructuredGraph graph) {
+            DebugContext debug = graph.getDebug();
             for (Node n : graph.getNodes()) {
-                Debug.log("%s", n);
+                debug.log("%s", n);
             }
         }
 
@@ -175,9 +187,10 @@
         @Override
         @SuppressWarnings("try")
         protected void run(StructuredGraph graph) {
-            try (Indent i = Debug.logAndIndent("%s", graph)) {
+            DebugContext debug = graph.getDebug();
+            try (Indent i = debug.logAndIndent("%s", graph)) {
                 for (Node n : graph.getNodes()) {
-                    Debug.log("%s", n);
+                    debug.log("%s", n);
                 }
             }
         }
@@ -188,7 +201,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.dump(Debug.BASIC_LEVEL, graph, "%s", graph);
+            DebugContext debug = graph.getDebug();
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "%s", graph);
         }
 
     }
@@ -197,7 +211,8 @@
 
         @Override
         protected void run(StructuredGraph graph) {
-            Debug.verify(graph, "%s", graph);
+            DebugContext debug = graph.getDebug();
+            debug.verify(graph, "%s", graph);
         }
 
     }
@@ -332,12 +347,14 @@
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
         graphBuilderSuite.appendPhase(new GraphBuilderPhase(config));
         HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE);
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
         for (Method m : c.getDeclaredMethods()) {
             if (!Modifier.isNative(m.getModifiers()) && !Modifier.isAbstract(m.getModifiers())) {
                 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-                StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
+                StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
                 graphBuilderSuite.apply(graph, context);
-                try (DebugConfigScope s = Debug.disableIntercept()) {
+                try (DebugCloseable s = debug.disableIntercept()) {
                     new VerifyDebugUsage().apply(graph, context);
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyVirtualizableTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyVirtualizableTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,12 +29,11 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.java.GraphBuilderPhase;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -47,6 +46,7 @@
 import org.graalvm.compiler.nodes.java.ArrayLengthNode;
 import org.graalvm.compiler.nodes.spi.Virtualizable;
 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.VerifyPhase.VerificationError;
@@ -54,6 +54,7 @@
 import org.graalvm.compiler.phases.util.Providers;
 import org.graalvm.compiler.phases.verify.VerifyVirtualizableUsage;
 import org.graalvm.compiler.runtime.RuntimeProvider;
+import org.junit.Test;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -270,12 +271,14 @@
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
         graphBuilderSuite.appendPhase(new GraphBuilderPhase(config));
         HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE);
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
         for (Method m : c.getDeclaredMethods()) {
             if (!Modifier.isNative(m.getModifiers()) && !Modifier.isAbstract(m.getModifiers())) {
                 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-                StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
+                StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
                 graphBuilderSuite.apply(graph, context);
-                try (DebugConfigScope s = Debug.disableIntercept()) {
+                try (DebugCloseable s = debug.disableIntercept()) {
                     new VerifyVirtualizableUsage().apply(graph, context);
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/backend/AllocatorTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/backend/AllocatorTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,15 +24,8 @@
 
 import java.util.HashSet;
 
-import jdk.vm.ci.code.Register;
-import jdk.vm.ci.code.ValueUtil;
-import jdk.vm.ci.meta.Value;
-
-import org.junit.Assert;
-
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRValueUtil;
@@ -40,23 +33,29 @@
 import org.graalvm.compiler.lir.ValueProcedure;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.junit.Assert;
+
+import jdk.vm.ci.code.Register;
+import jdk.vm.ci.code.ValueUtil;
+import jdk.vm.ci.meta.Value;
 
 public class AllocatorTest extends BackendTest {
 
     @SuppressWarnings("try")
     protected void testAllocation(String snippet, final int expectedRegisters, final int expectedRegRegMoves, final int expectedSpillMoves) {
         final StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        try (Scope s = Debug.scope("AllocatorTest", graph, graph.method(), getCodeCache())) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("AllocatorTest", graph, graph.method(), getCodeCache())) {
             final RegisterStats stats = new RegisterStats(getLIRGenerationResult(graph).getLIR());
-            try (Scope s2 = Debug.scope("Assertions", stats.lir)) {
+            try (DebugContext.Scope s2 = debug.scope("Assertions", stats.lir)) {
                 Assert.assertEquals("register count", expectedRegisters, stats.registers.size());
                 Assert.assertEquals("reg-reg moves", expectedRegRegMoves, stats.regRegMoves);
                 Assert.assertEquals("spill moves", expectedSpillMoves, stats.spillMoves);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         } catch (Throwable e) {
-            throw Debug.handle(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/backend/BackendTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/backend/BackendTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,15 @@
  */
 package org.graalvm.compiler.core.test.backend;
 
-import jdk.vm.ci.code.Architecture;
-
 import org.graalvm.compiler.core.GraalCompiler;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 
+import jdk.vm.ci.code.Architecture;
+
 public abstract class BackendTest extends GraalCompilerTest {
 
     public BackendTest() {
@@ -44,10 +43,11 @@
 
     @SuppressWarnings("try")
     protected LIRGenerationResult getLIRGenerationResult(final StructuredGraph graph) {
-        try (Scope s = Debug.scope("FrontEnd")) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("FrontEnd")) {
             GraalCompiler.emitFrontEnd(getProviders(), getBackend(), graph, getDefaultGraphBuilderSuite(), OptimisticOptimizations.NONE, graph.getProfilingInfo(), createSuites(graph.getOptions()));
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
 
         LIRGenerationResult lirGen = GraalCompiler.emitLIR(getBackend(), graph, null, null, createLIRSuites(graph.getOptions()));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import java.io.PrintStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.graalvm.compiler.core.common.util.Util;
-import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.DebugVerifyHandler;
-import org.graalvm.compiler.debug.DelegatingDebugConfig;
-import org.graalvm.compiler.debug.DelegatingDebugConfig.Feature;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl.CompilationData;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter;
-import org.graalvm.compiler.nodes.InvokeNode;
-import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.calc.BinaryNode;
-import org.graalvm.compiler.nodes.calc.FixedBinaryNode;
-import org.graalvm.compiler.nodes.calc.MulNode;
-import org.graalvm.compiler.nodes.calc.ShiftNode;
-import org.graalvm.compiler.nodes.calc.SignedDivNode;
-import org.graalvm.compiler.nodes.calc.SubNode;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.BasePhase;
-import org.graalvm.compiler.phases.Phase;
-import org.graalvm.compiler.phases.PhaseSuite;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
-import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase;
-import org.graalvm.compiler.phases.schedule.SchedulePhase;
-import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
-import org.graalvm.compiler.phases.tiers.HighTierContext;
-import org.graalvm.compiler.phases.tiers.Suites;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public abstract class MethodMetricsTest extends GraalCompilerTest {
-    static class TestApplication {
-        public static int m01(int x, int y) {
-            return x + y;
-        }
-
-        public static int m02(int x, int y) {
-            return x * y;
-        }
-
-        public static int m03(int x, int y) {
-            return x ^ y;
-        }
-
-        public static int m04(int x, int y) {
-            return x >> y;
-        }
-
-        public static int m05(int x, int y) {
-            return x >>> y;
-        }
-
-        public static int m06(int x, int y) {
-            return x << y;
-        }
-
-        public static int m07(int x, int y) {
-            return x > y ? 0 : 1;
-        }
-
-        public static int m08(int x, int y) {
-            return x % y;
-        }
-
-        public static int m09(int x, int y) {
-            return x / y;
-        }
-
-        public static int m10(int x, int y) {
-            return x - y;
-        }
-
-    }
-
-    public static final Class<?>[] testSignature = new Class<?>[]{int.class, int.class};
-    public static final Object[] testArgs = new Object[]{10, 10};
-
-    static class MethodMetricPhases {
-        static class CountingAddPhase extends Phase {
-
-            // typically those global metrics would be static final, but we need new timers every
-            // invocation if we override the debugvaluefactory
-            private final DebugCounter globalCounter = Debug.counter("GlobalMetric");
-            private final DebugTimer globalTimer = Debug.timer("GlobalTimer");
-
-            @Override
-            @SuppressWarnings("try")
-            protected void run(StructuredGraph graph) {
-                try (DebugCloseable d = globalTimer.start()) {
-                    ResolvedJavaMethod method = graph.method();
-                    DebugMethodMetrics mm = Debug.methodMetrics(method);
-                    mm.addToMetric(graph.getNodes().filter(InvokeNode.class).count(), "Invokes");
-                    mm.incrementMetric("PhaseRunsOnMethod");
-                    globalCounter.increment();
-                }
-            }
-        }
-
-        static class CountingShiftPhase extends Phase {
-            @Override
-            protected void run(StructuredGraph graph) {
-                Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(ShiftNode.class).count(), "Shifts");
-            }
-        }
-
-        static class CountingMulPhase extends Phase {
-            @Override
-            protected void run(StructuredGraph graph) {
-                Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(MulNode.class).count(), "Muls");
-            }
-        }
-
-        static class CountingSubPhase extends Phase {
-            @Override
-            protected void run(StructuredGraph graph) {
-                Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(SubNode.class).count(), "Subs");
-            }
-        }
-
-        static class CountingDivPhase extends Phase {
-            @Override
-            protected void run(StructuredGraph graph) {
-                Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(SignedDivNode.class).count(), "Divs");
-            }
-        }
-
-        static class CountingBinOpPhase extends Phase {
-            @Override
-            protected void run(StructuredGraph graph) {
-                Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(x -> x instanceof BinaryNode || x instanceof FixedBinaryNode).count(), "BinOps");
-            }
-        }
-
-        static class ScopeTestPhase extends Phase {
-            // typically those global metrics would be static final, but we need new timers every
-            // invocation if we override the debugvaluefactory
-            private final DebugTimer timer = Debug.timer("GlobalTimer1");
-            private final DebugTimer scopedTimer = Debug.timer("GlobalTimer2");
-            private final DebugTimer scopedScopedTimer = Debug.timer("GlobalTimer3");
-            private final DebugTimer scopedScopedScopeTimer = Debug.timer("GlobalTimer4");
-
-            private final DebugTimer timer1 = Debug.timer("GlobalTimer1_WithoutInlineEnhancement");
-            private final DebugTimer scopedTimer1 = Debug.timer("GlobalTimer2_WithoutInlineEnhancement");
-            private final DebugTimer scopedScopedTimer1 = Debug.timer("GlobalTimer3_WithoutInlineEnhancement");
-            private final DebugTimer scopedScopedScopeTimer1 = Debug.timer("GlobalTimer4_WithoutInlineEnhancement");
-
-            @Override
-            @SuppressWarnings("try")
-            protected void run(StructuredGraph graph) {
-                // we are in an enhanced debug scope from graal compiler
-                // now we open multiple inlining scopes, record their time
-                try (DebugCloseable c1 = timer.start()) {
-                    try (DebugCloseable c2 = scopedTimer.start()) {
-                        try (DebugCloseable c3 = scopedScopedTimer.start()) {
-                            // do sth unnecessary long allocating many inlinee scopes
-                            for (int i = 0; i < 50; i++) {
-                                try (Debug.Scope s1 = Debug.methodMetricsScope("InlineEnhancement1", MethodMetricsInlineeScopeInfo.create(graph.method()), false)) {
-                                    try (DebugCloseable c4 = scopedScopedScopeTimer.start()) {
-                                        new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph);
-                                        // double scoped inlinee scopes should not make problems
-                                        // with the data
-                                        try (Debug.Scope s2 = Debug.methodMetricsScope("InlineEnhancement2", MethodMetricsInlineeScopeInfo.create(graph.method()),
-                                                        false)) {
-                                            new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-
-                // now lets try different counters without the inline enhancement
-                try (DebugCloseable c1 = timer1.start()) {
-                    try (DebugCloseable c2 = scopedTimer1.start()) {
-                        try (DebugCloseable c3 = scopedScopedTimer1.start()) {
-                            // do sth unnecessary long allocating many inlinee scopes
-                            for (int i = 0; i < 50; i++) {
-                                try (DebugCloseable c4 = scopedScopedScopeTimer1.start()) {
-                                    new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph);
-                                    new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-        }
-    }
-
-    static DebugConfig overrideGraalDebugConfig(PrintStream log, String methodFilter, String methodMeter) {
-        List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
-        List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
-        OptionValues options = getInitialOptions();
-        GraalDebugConfig debugConfig = new GraalDebugConfig(
-                        options,
-                        GraalDebugConfig.Options.Log.getValue(options),
-                        GraalDebugConfig.Options.Count.getValue(options),
-                        GraalDebugConfig.Options.TrackMemUse.getValue(options),
-                        GraalDebugConfig.Options.Time.getValue(options),
-                        GraalDebugConfig.Options.Dump.getValue(options),
-                        GraalDebugConfig.Options.Verify.getValue(options),
-                        methodFilter,
-                        methodMeter,
-                        log, dumpHandlers, verifyHandlers);
-        return debugConfig;
-    }
-
-    abstract Phase additionalPhase();
-
-    @Override
-    protected Suites createSuites(OptionValues options) {
-        Suites ret = super.createSuites(options);
-        ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(ConvertDeoptimizeToGuardPhase.class, true);
-        PhaseSuite.findNextPhase(iter, CanonicalizerPhase.class);
-        iter.add(additionalPhase());
-        return ret;
-    }
-
-    @Test
-    @SuppressWarnings("try")
-    public void test() throws Throwable {
-        try (DebugConfigScope s = Debug.setConfig(getConfig())) {
-            executeMethod(TestApplication.class.getMethod("m01", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m02", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m03", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m04", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m05", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m06", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m07", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m08", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m09", testSignature), null, testArgs);
-            executeMethod(TestApplication.class.getMethod("m10", testSignature), null, testArgs);
-            assertValues();
-        }
-    }
-
-    void executeMethod(Method m, Object receiver, Object... args) {
-        OptionValues options = new OptionValues(getInitialOptions(), MethodMetricsPrinter.Options.MethodMeterPrintAscii, true);
-        test(options, asResolvedJavaMethod(m), receiver, args);
-    }
-
-    @Before
-    public void rememberScopeId() {
-        scopeIdBeforeAccess = DebugScope.getCurrentGlobalScopeId();
-    }
-
-    @After
-    public void clearMMCache() {
-        MethodMetricsImpl.clearMM();
-    }
-
-    abstract DebugConfig getConfig();
-
-    abstract void assertValues() throws Throwable;
-
-    @SuppressWarnings("unchecked")
-    private static Map<ResolvedJavaMethod, CompilationData> readMethodMetricsImplData() {
-        Map<ResolvedJavaMethod, CompilationData> threadLocalMap = null;
-        for (Field f : MethodMetricsImpl.class.getDeclaredFields()) {
-            if (f.getName().equals("threadEntries")) {
-                Util.setAccessible(f, true);
-                Object map;
-                try {
-                    map = ((ThreadLocal<?>) f.get(null)).get();
-                } catch (Throwable t) {
-                    throw new RuntimeException(t);
-                }
-                threadLocalMap = (Map<ResolvedJavaMethod, CompilationData>) map;
-                break;
-            }
-        }
-        return threadLocalMap;
-    }
-
-    private long scopeIdBeforeAccess;
-    private long scopeIdAfterAccess;
-
-    protected long readValFromCurrThread(ResolvedJavaMethod method, String metricName) {
-
-        Map<ResolvedJavaMethod, CompilationData> threadLocalMap = readMethodMetricsImplData();
-        assert threadLocalMap != null;
-        CompilationData compilationData = threadLocalMap.get(method);
-        assert compilationData != null;
-        Map<Long, Map<String, Long>> compilations = compilationData.getCompilations();
-        List<Map<String, Long>> compilationEntries = new ArrayList<>();
-        compilations.forEach((x, y) -> {
-            if (x >= scopeIdBeforeAccess && x <= scopeIdAfterAccess) {
-                compilationEntries.add(y);
-            }
-        });
-        List<Map<String, Long>> listView = compilationEntries.stream().filter(x -> x.size() > 0).collect(Collectors.toList());
-        assert listView.size() <= 1 : "There must be at most one none empty compilation data point present:" + listView.size();
-        /*
-         * NOTE: Using the pre-generation of compilation entries for a method has the disadvantage
-         * that during testing we have different points in time when we request the metric. First,
-         * properly, when we use it and then when we want to know the result, but when we check the
-         * result the debug context no longer holds a correct scope with the unique id, so we return
-         * the first compilation entry that is not empty.
-         */
-        Map<String, Long> entries = listView.size() > 0 ? listView.get(0) : null;
-        Long res = entries != null ? entries.get(metricName) : null;
-        return res != null ? res : 0;
-    }
-
-    @SuppressWarnings("try")
-    void assertValues(String metricName, long[] vals) {
-        scopeIdAfterAccess = DebugScope.getCurrentGlobalScopeId();
-        try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().enable(Feature.METHOD_METRICS))) {
-            Assert.assertEquals(vals[0], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m01", testSignature)), metricName));
-            Assert.assertEquals(vals[1], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m02", testSignature)), metricName));
-            Assert.assertEquals(vals[2], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m03", testSignature)), metricName));
-            Assert.assertEquals(vals[3], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m04", testSignature)), metricName));
-            Assert.assertEquals(vals[4], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m05", testSignature)), metricName));
-            Assert.assertEquals(vals[5], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m06", testSignature)), metricName));
-            Assert.assertEquals(vals[6], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m07", testSignature)), metricName));
-            Assert.assertEquals(vals[7], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m08", testSignature)), metricName));
-            Assert.assertEquals(vals[8], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m09", testSignature)), metricName));
-        } catch (Throwable t) {
-            throw new RuntimeException(t);
-        }
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest1.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest1 extends MethodMetricsTest {
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingAddPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingAddPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("PhaseRunsOnMethod", new long[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
-    }
-
-    @Override
-    @Test
-    public void test() throws Throwable {
-        super.test();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest2.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest2 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingShiftPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingShiftPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("Shifts", new long[]{0, 0, 0, 1, 1, 1, 0, 0, 0, 0});
-    }
-
-    @Test
-    @Override
-    public void test() throws Throwable {
-        super.test();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest3.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest3 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingMulPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingMulPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("Muls", new long[]{0, 1, 0, 0, 0, 0, 0, 0, 0, 0});
-    }
-
-    @Override
-    @Test
-    public void test() throws Throwable {
-        super.test();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest4.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest4 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingSubPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingSubPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("Subs", new long[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
-    }
-
-    @Override
-    @Test
-    public void test() throws Throwable {
-        super.test();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest5.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest5 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingDivPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingDivPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("Divs", new long[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 0});
-    }
-
-    @Override
-    @Test
-    public void test() throws Throwable {
-        super.test();
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTest6.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.phases.Phase;
-
-public class MethodMetricsTest6 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingBinOpPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        return overrideGraalDebugConfig(System.out, "MethodMetricsTest$TestApplication.*", "CountingBinOpPhase");
-    }
-
-    @Override
-    void assertValues() throws Throwable {
-        assertValues("BinOps", new long[]{1, 1, 1, 1, 1, 1, 0, 1, 1, 1});
-    }
-
-    @Override
-    @Test
-    public void test() throws Throwable {
-        super.test();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTestInterception01.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.DebugValueFactory;
-import org.graalvm.compiler.debug.DebugVerifyHandler;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.internal.CounterImpl;
-import org.graalvm.compiler.debug.internal.MemUseTrackerImpl;
-import org.graalvm.compiler.debug.internal.TimerImpl;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.Phase;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-// intercepting metrics
-public class MethodMetricsTestInterception01 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.CountingAddPhase();
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
-        List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
-        OptionValues options = getInitialOptions();
-        GraalDebugConfig debugConfig = new GraalDebugConfig(
-                        options,
-                        GraalDebugConfig.Options.Log.getValue(options),
-                        "CountingAddPhase",
-                        GraalDebugConfig.Options.TrackMemUse.getValue(options),
-                        "CountingAddPhase",
-                        GraalDebugConfig.Options.Dump.getValue(options),
-                        GraalDebugConfig.Options.Verify.getValue(options),
-                        "MethodMetricsTest$TestApplication.*",
-                        "CountingAddPhase",
-                        System.out, dumpHandlers, verifyHandlers);
-        return debugConfig;
-    }
-
-    private DebugValueFactory factory;
-
-    @Test
-    @Override
-    @SuppressWarnings("try")
-    public void test() throws Throwable {
-        factory = Debug.getDebugValueFactory();
-        Debug.setDebugValueFactory(new DebugValueFactory() {
-            @Override
-            public DebugTimer createTimer(String name, boolean conditional) {
-                return new TimerImpl(name, conditional, true);
-            }
-
-            @Override
-            public DebugCounter createCounter(String name, boolean conditional) {
-                return CounterImpl.create(name, conditional, true);
-            }
-
-            @Override
-            public DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method) {
-                return MethodMetricsImpl.getMethodMetrics(method);
-            }
-
-            @Override
-            public DebugMemUseTracker createMemUseTracker(String name, boolean conditional) {
-                return new MemUseTrackerImpl(name, conditional, true);
-            }
-        });
-        super.test();
-
-    }
-
-    @Override
-    public void afterTest() {
-        super.afterTest();
-        Debug.setDebugValueFactory(factory);
-    }
-
-    @Override
-    @SuppressWarnings("try")
-    void assertValues() throws Throwable {
-        assertValues("GlobalMetric", new long[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/MethodMetricsTestInterception02.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.DebugValueFactory;
-import org.graalvm.compiler.debug.DebugVerifyHandler;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.Phase;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-// intercepting metrics
-public class MethodMetricsTestInterception02 extends MethodMetricsTest {
-
-    @Override
-    protected Phase additionalPhase() {
-        return new MethodMetricPhases.ScopeTestPhase();
-    }
-
-    private DebugValueFactory factory;
-
-    public void setFactory() {
-        /*
-         * setting a custom debug value factory creating a constant timer for checking scope
-         * creation and inlining scopes with metric intercepting works
-         */
-        factory = Debug.getDebugValueFactory();
-        Debug.setDebugValueFactory(new DebugValueFactory() {
-            @Override
-            public DebugTimer createTimer(String name, boolean conditional) {
-                // can still use together with real timer
-                // TimerImpl realTimer = new TimerImpl(name, conditional, true);
-                return new DebugTimer() {
-                    int runs = 0;
-
-                    // private DebugCloseable t;
-
-                    @Override
-                    public DebugCloseable start() {
-                        // t = realTimer.start();
-                        return new DebugCloseable() {
-                            @Override
-                            public void close() {
-                                // t.close();
-                                runs++;
-                                MethodMetricsImpl.addToCurrentScopeMethodMetrics(name, 1);
-                            }
-                        };
-                    }
-
-                    @Override
-                    public void setConditional(boolean flag) {
-
-                    }
-
-                    @Override
-                    public boolean isConditional() {
-                        return false;
-                    }
-
-                    @Override
-                    public TimeUnit getTimeUnit() {
-                        return TimeUnit.MILLISECONDS;
-                    }
-
-                    @Override
-                    public long getCurrentValue() {
-                        return runs;
-                    }
-                };
-            }
-
-            @Override
-            public DebugCounter createCounter(String name, boolean conditional) {
-                return factory.createCounter(name, conditional);
-            }
-
-            @Override
-            public DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method) {
-                return factory.createMethodMetrics(method);
-            }
-
-            @Override
-            public DebugMemUseTracker createMemUseTracker(String name, boolean conditional) {
-                return factory.createMemUseTracker(name, conditional);
-            }
-        });
-    }
-
-    @Test
-    @Override
-    public void test() throws Throwable {
-        setFactory();
-        super.test();
-    }
-
-    @Override
-    public void afterTest() {
-        super.afterTest();
-        Debug.setDebugValueFactory(factory);
-    }
-
-    @Override
-    DebugConfig getConfig() {
-        List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
-        List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
-        OptionValues options = getInitialOptions();
-        GraalDebugConfig debugConfig = new GraalDebugConfig(
-                        options,
-                        GraalDebugConfig.Options.Log.getValue(options),
-                        ""/* unscoped meter */,
-                        GraalDebugConfig.Options.TrackMemUse.getValue(options),
-                        ""/* unscoped time */,
-                        GraalDebugConfig.Options.Dump.getValue(options),
-                        GraalDebugConfig.Options.Verify.getValue(options),
-                        null /* no method filter */,
-                        "" /* unscoped method metering */,
-                        System.out, dumpHandlers, verifyHandlers);
-        return debugConfig;
-    }
-
-    @Override
-    @SuppressWarnings("try")
-    void assertValues() throws Throwable {
-        assertValues("GlobalTimer4_WithoutInlineEnhancement", new long[]{50, 50, 50, 50, 50, 50, 50, 50, 50, 50});
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/debug/VerifyMethodMetricsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2015, 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.core.test.debug;
-
-import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-import org.graalvm.compiler.api.test.Graal;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.graph.Node;
-import org.graalvm.compiler.java.GraphBuilderPhase;
-import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
-import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
-import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.Phase;
-import org.graalvm.compiler.phases.PhaseSuite;
-import org.graalvm.compiler.phases.VerifyPhase.VerificationError;
-import org.graalvm.compiler.phases.tiers.HighTierContext;
-import org.graalvm.compiler.phases.util.Providers;
-import org.graalvm.compiler.phases.verify.VerifyDebugUsage;
-import org.graalvm.compiler.runtime.RuntimeProvider;
-import org.junit.Test;
-
-import jdk.vm.ci.meta.MetaAccessProvider;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-/**
- *
- * Tests to verify that the usage of method metrics does not generate compile time overhead through
- * eager evaluation of arguments.
- */
-public class VerifyMethodMetricsTest {
-
-    private static class InvalidCCP_ToString01Inc extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.incrementMetric(n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_Concat01Inc extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.incrementMetric("a" + n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_ToString02Inc extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.incrementMetric("%s", n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_Concat02Inc extends Phase {
-        private final String s = this.getClass().toGenericString();
-
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.incrementMetric("%s%s", "a" + s, n);
-            }
-        }
-    }
-
-    private static class ValidCCP_ToStringInc extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "%s", n);
-            }
-        }
-    }
-
-    private static class ValidCCP_ConcatInc extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.incrementMetric("%s%s", "a", n);
-            }
-        }
-    }
-
-    private static class InvalidCCP_ToString01Add extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_Concat01Add extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "a" + n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_ToString02Add extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "%s", n.toString());
-            }
-        }
-    }
-
-    private static class InvalidCCP_Concat02Add extends Phase {
-        private final String s = this.getClass().toGenericString();
-
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "%s%s", "a" + s, n);
-            }
-        }
-    }
-
-    private static class ValidCCP_ToStringAdd extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "%s", n);
-            }
-        }
-    }
-
-    private static class ValidCCP_ConcatAdd extends Phase {
-        @Override
-        protected void run(StructuredGraph graph) {
-            DebugMethodMetrics m = Debug.methodMetrics(graph.method());
-            for (Node n : graph.getNodes()) {
-                m.addToMetric(1, "%s%s", "a", n);
-            }
-        }
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidToString01Add() {
-        testDebugUsageClass(InvalidCCP_ToString01Add.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidConcat01Add() {
-        testDebugUsageClass(InvalidCCP_Concat01Add.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidToString02Add() {
-        testDebugUsageClass(InvalidCCP_ToString02Add.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidConcat02Add() {
-        testDebugUsageClass(InvalidCCP_Concat02Add.class);
-    }
-
-    @Test
-    public void testLogValidToStringAdd() {
-        testDebugUsageClass(ValidCCP_ToStringAdd.class);
-    }
-
-    @Test
-    public void testLogValidConcatAdd() {
-        testDebugUsageClass(ValidCCP_ConcatAdd.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidToString01Inc() {
-        testDebugUsageClass(InvalidCCP_ToString01Inc.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidConcat01Inc() {
-        testDebugUsageClass(InvalidCCP_Concat01Inc.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidToString02Inc() {
-        testDebugUsageClass(InvalidCCP_ToString02Inc.class);
-    }
-
-    @Test(expected = VerificationError.class)
-    public void testLogInvalidConcat02Inc() {
-        testDebugUsageClass(InvalidCCP_Concat02Inc.class);
-    }
-
-    @Test
-    public void testLogValidToStringInc() {
-        testDebugUsageClass(ValidCCP_ToStringInc.class);
-    }
-
-    @Test
-    public void testLogValidConcatInc() {
-        testDebugUsageClass(ValidCCP_ConcatInc.class);
-    }
-
-    @SuppressWarnings("try")
-    private static void testDebugUsageClass(Class<?> c) {
-        RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class);
-        Providers providers = rt.getHostBackend().getProviders();
-        MetaAccessProvider metaAccess = providers.getMetaAccess();
-        PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>();
-        Plugins plugins = new Plugins(new InvocationPlugins());
-        GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
-        graphBuilderSuite.appendPhase(new GraphBuilderPhase(config));
-        HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE);
-        for (Method m : c.getDeclaredMethods()) {
-            if (!Modifier.isNative(m.getModifiers()) && !Modifier.isAbstract(m.getModifiers())) {
-                ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
-                StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
-                graphBuilderSuite.apply(graph, context);
-                try (DebugConfigScope s = Debug.disableIntercept()) {
-                    new VerifyDebugUsage().apply(graph, context);
-                }
-            }
-        }
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/MonitorDeoptTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/MonitorDeoptTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -189,7 +189,7 @@
         removeLoopSafepoint(graph);
 
         CompilationResult compilationResult = compile(javaMethod, graph);
-        final InstalledCode installedCode = getBackend().createDefaultInstalledCode(javaMethod, compilationResult);
+        final InstalledCode installedCode = getBackend().createDefaultInstalledCode(graph.getDebug(), javaMethod, compilationResult);
 
         final Monitor monitor = new Monitor();
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/SynchronizedMethodDeoptimizationTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/SynchronizedMethodDeoptimizationTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.core.test.deopt;
 
+import org.junit.Assume;
 import org.junit.Test;
 
 import org.graalvm.compiler.core.test.GraalCompilerTest;
@@ -44,6 +45,9 @@
 
     @Test
     public void test1() {
+        // https://bugs.openjdk.java.net/browse/JDK-8182755
+        Assume.assumeTrue(Java8OrEarlier);
+
         test("testMethodSynchronized", "test");
         test("testMethodSynchronized", (Object) null);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,14 +24,8 @@
 
 import java.util.List;
 
-import jdk.vm.ci.meta.JavaConstant;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-import org.junit.Assert;
-
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -43,6 +37,10 @@
 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 jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 //JaCoCo Exclude
 
@@ -154,8 +152,9 @@
     @SuppressWarnings("try")
     protected void prepareGraph(String snippet, boolean iterativeEscapeAnalysis) {
         ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
-        try (Scope s = Debug.scope(getClass(), method, getCodeCache())) {
-            graph = parseEager(method, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope(getClass(), method, getCodeCache())) {
+            graph = parseEager(method, AllowAssumptions.YES, debug);
             context = getDefaultHighTierContext();
             new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
             new DeadCodeEliminationPhase().apply(graph);
@@ -163,7 +162,7 @@
             new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
             returnNodes = graph.getNodes(ReturnNode.TYPE).snapshot();
         } catch (Throwable e) {
-            throw Debug.handle(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/ea/PoorMansEATest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PoorMansEATest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,8 @@
  */
 package org.graalvm.compiler.core.test.ea;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.FrameState;
@@ -40,6 +37,7 @@
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Test;
 
 /**
  * Tests {@link AbstractNewObjectNode#simplify(org.graalvm.compiler.graph.spi.SimplifierTool)}.
@@ -63,7 +61,8 @@
 
     @SuppressWarnings("try")
     private void test(final String snippet) {
-        try (Scope s = Debug.scope("PoorMansEATest", new DebugDumpScope(snippet))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("PoorMansEATest", new DebugDumpScope(snippet))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
             HighTierContext highTierContext = getDefaultHighTierContext();
             new InliningPhase(new CanonicalizerPhase()).apply(graph, highTierContext);
@@ -82,7 +81,7 @@
             }
             new CanonicalizerPhase().apply(graph, context);
         } catch (Throwable e) {
-            throw Debug.handle(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/inlining/InliningTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,21 +22,15 @@
  */
 package org.graalvm.compiler.core.test.inlining;
 
-import jdk.vm.ci.code.site.InfopointReason;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
@@ -44,6 +38,11 @@
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import jdk.vm.ci.code.site.InfopointReason;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 public class InliningTest extends GraalCompilerTest {
 
@@ -236,24 +235,26 @@
 
     @SuppressWarnings("try")
     private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
-        try (Scope s = Debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
             ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
-            StructuredGraph graph = eagerInfopointMode ? parseDebug(method, AllowAssumptions.YES) : parseEager(method, AllowAssumptions.YES);
-            try (Scope s2 = Debug.scope("Inlining", graph)) {
+            Builder builder = builder(method, AllowAssumptions.YES, debug);
+            StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite());
+            try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) {
                 PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode
                                 ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
                                 : getDefaultGraphBuilderSuite();
                 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
-                Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
                 new CanonicalizerPhase().apply(graph, context);
                 new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
-                Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
                 new CanonicalizerPhase().apply(graph, context);
                 new DeadCodeEliminationPhase().apply(graph);
                 return graph;
             }
         } catch (Throwable e) {
-            throw Debug.handle(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/inlining/NestedLoopEffectsPhaseComplexityTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,13 +25,14 @@
 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional;
 
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.BasePhase;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
@@ -100,10 +101,9 @@
     }
 
     private void testAndTime(String snippet) {
-        initializeForTimeout();
         for (int i = InliningCountLowerBound; i < InliningCountUpperBound; i++) {
             StructuredGraph g1 = prepareGraph(snippet, i);
-            StructuredGraph g2 = (StructuredGraph) g1.copy();
+            StructuredGraph g2 = (StructuredGraph) g1.copy(g1.getDebug());
             ResolvedJavaMethod method = g1.method();
             long elapsedRE = runAndTimePhase(g1, new EarlyReadEliminationPhase(new CanonicalizerPhase()));
             long elapsedPEA = runAndTimePhase(g2, new PartialEscapePhase(true, new CanonicalizerPhase(), g1.getOptions()));
@@ -121,7 +121,8 @@
         long start = System.currentTimeMillis();
         phase.apply(g, context);
         long end = System.currentTimeMillis();
-        Debug.dump(Debug.DETAILED_LEVEL, g, "After %s", phase.contractorName());
+        DebugContext debug = g.getDebug();
+        debug.dump(DebugContext.DETAILED_LEVEL, g, "After %s", phase.contractorName());
         return end - start;
     }
 
@@ -138,13 +139,14 @@
             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_LEVEL, callerGraph, "After inlining %s into %s iteration %d", calleeMethod, callerMethod, i);
+            callerGraph.getDebug().dump(DebugContext.DETAILED_LEVEL, callerGraph, "After inlining %s into %s iteration %d", calleeMethod, callerMethod, i);
         }
         return callerGraph;
     }
 
-    private static StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer) {
-        StructuredGraph newGraph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.NO).method(method).build();
+    private StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer) {
+        OptionValues options = getInitialOptions();
+        StructuredGraph newGraph = new StructuredGraph.Builder(options, getDebugContext(options), AllowAssumptions.NO).method(method).build();
         context.getGraphBuilderSuite().apply(newGraph, context);
         new DeadCodeEliminationPhase(Optional).apply(newGraph);
         canonicalizer.apply(newGraph, context);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/InvokeGraal.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/InvokeGraal.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,6 +24,7 @@
 
 import static org.graalvm.compiler.core.common.CompilationRequestIdentifier.asCompilationRequest;
 import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
+
 import java.lang.reflect.Method;
 
 import org.graalvm.compiler.api.test.Graal;
@@ -31,8 +32,8 @@
 import org.graalvm.compiler.core.GraalCompiler;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 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.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
 import org.graalvm.compiler.lir.phases.LIRSuites;
@@ -85,7 +86,8 @@
         /* Create a unique compilation identifier, visible in IGV. */
         CompilationIdentifier compilationId = backend.getCompilationIdentifier(method);
         OptionValues options = getInitialOptions();
-        try (Scope s = Debug.scope("compileAndInstallMethod", new DebugDumpScope(String.valueOf(compilationId), true))) {
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+        try (DebugContext.Scope s = debug.scope("compileAndInstallMethod", new DebugDumpScope(String.valueOf(compilationId), true))) {
 
             /*
              * The graph that is compiled. We leave it empty (no nodes added yet). This means that
@@ -93,7 +95,7 @@
              * that we want the compilation to make optimistic assumptions about runtime state such
              * as the loaded class hierarchy.
              */
-            StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).method(method).compilationId(compilationId).build();
+            StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(method).compilationId(compilationId).build();
 
             /*
              * The phases used to build the graph. Usually this is just the GraphBuilderPhase. If
@@ -131,9 +133,9 @@
              * Install the compilation result into the VM, i.e., copy the byte[] array that contains
              * the machine code into an actual executable memory location.
              */
-            return backend.addInstalledCode(method, asCompilationRequest(compilationId), compilationResult);
+            return backend.addInstalledCode(debug, method, asCompilationRequest(compilationId), compilationResult);
         } catch (Throwable ex) {
-            throw Debug.handle(ex);
+            throw debug.handle(ex);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/StaticAnalysis.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/tutorial/StaticAnalysis.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,9 +32,9 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.debug.Debug.Scope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.java.GraphBuilderPhase;
@@ -58,6 +58,7 @@
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
 import org.graalvm.compiler.nodes.spi.StampProvider;
 import org.graalvm.compiler.nodes.util.GraphUtil;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.graph.StatelessPostOrderNodeIterator;
 
@@ -240,12 +241,14 @@
                  * Build the Graal graph for the method using the bytecode parser provided by Graal.
                  */
 
-                StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).method(method).build();
+                OptionValues options = getInitialOptions();
+                DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+                StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).build();
                 /*
                  * Support for graph dumping, IGV uses this information to show the method name of a
                  * graph.
                  */
-                try (Scope scope = Debug.scope("graph building", graph)) {
+                try (DebugContext.Scope scope = debug.scope("graph building", graph)) {
                     /*
                      * We want all types to be resolved by the graph builder, i.e., we want classes
                      * referenced by the bytecodes to be loaded and initialized. Since we do not run
@@ -271,7 +274,7 @@
                     GraphBuilderPhase.Instance graphBuilder = new GraphBuilderPhase.Instance(metaAccess, stampProvider, null, null, graphBuilderConfig, optimisticOpts, null);
                     graphBuilder.apply(graph);
                 } catch (Throwable ex) {
-                    Debug.handle(ex);
+                    debug.handle(ex);
                 }
 
                 /*
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThread.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThread.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,42 +22,21 @@
  */
 package org.graalvm.compiler.core;
 
-import org.graalvm.compiler.core.CompilerThreadFactory.DebugConfigAccess;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-
 /**
- * A compiler thread is a daemon thread that runs at {@link Thread#MAX_PRIORITY} and executes in the
- * context of a thread-local {@linkplain GraalDebugConfig debug configuration}.
+ * A compiler thread is a daemon thread that runs at {@link Thread#MAX_PRIORITY}.
  */
 public class CompilerThread extends Thread {
 
-    private final DebugConfigAccess debugConfigAccess;
-
-    public CompilerThread(Runnable r, String namePrefix, DebugConfigAccess debugConfigAccess) {
+    public CompilerThread(Runnable r, String namePrefix) {
         super(r);
         this.setName(namePrefix + "-" + this.getId());
         this.setPriority(Thread.MAX_PRIORITY);
         this.setDaemon(true);
-        this.debugConfigAccess = debugConfigAccess;
     }
 
     @Override
     public void run() {
-        DebugConfig debugConfig = debugConfigAccess.getDebugConfig();
         setContextClassLoader(getClass().getClassLoader());
-        try {
-            super.run();
-        } finally {
-            if (debugConfig != null) {
-                for (DebugDumpHandler dumpHandler : debugConfig.dumpHandlers()) {
-                    try {
-                        dumpHandler.close();
-                    } catch (Throwable t) {
-                    }
-                }
-            }
-        }
+        super.run();
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThreadFactory.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilerThreadFactory.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,34 +24,19 @@
 
 import java.util.concurrent.ThreadFactory;
 
-import org.graalvm.compiler.debug.DebugConfig;
-
 /**
  * Facility for creating {@linkplain CompilerThread compiler threads}.
  */
 public class CompilerThreadFactory implements ThreadFactory {
 
-    /**
-     * Capability to get a thread-local debug configuration for the current thread.
-     */
-    public interface DebugConfigAccess {
-        /**
-         * Get a thread-local debug configuration for the current thread. This will be null if
-         * debugging is disabled.
-         */
-        DebugConfig getDebugConfig();
-    }
+    protected final String threadNamePrefix;
 
-    protected final String threadNamePrefix;
-    protected final DebugConfigAccess debugConfigAccess;
-
-    public CompilerThreadFactory(String threadNamePrefix, DebugConfigAccess debugConfigAccess) {
+    public CompilerThreadFactory(String threadNamePrefix) {
         this.threadNamePrefix = threadNamePrefix;
-        this.debugConfigAccess = debugConfigAccess;
     }
 
     @Override
     public Thread newThread(Runnable r) {
-        return new CompilerThread(r, threadNamePrefix, debugConfigAccess);
+        return new CompilerThread(r, threadNamePrefix);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,14 +33,12 @@
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.util.CompilationAlarm;
 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.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.MethodFilter;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsRootScopeInfo;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.alloc.OutOfRegistersException;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
@@ -85,11 +83,11 @@
  */
 public class GraalCompiler {
 
-    private static final DebugTimer CompilerTimer = Debug.timer("GraalCompiler");
-    private static final DebugTimer FrontEnd = Debug.timer("FrontEnd");
-    private static final DebugTimer BackEnd = Debug.timer("BackEnd");
-    private static final DebugTimer EmitLIR = Debug.timer("EmitLIR");
-    private static final DebugTimer EmitCode = Debug.timer("EmitCode");
+    private static final TimerKey CompilerTimer = DebugContext.timer("GraalCompiler").doc("Time spent in compilation (excludes code installation).");
+    private static final TimerKey FrontEnd = DebugContext.timer("FrontEnd").doc("Time spent processing HIR.");
+    private static final TimerKey EmitLIR = DebugContext.timer("EmitLIR").doc("Time spent generating LIR from HIR.");
+    private static final TimerKey EmitCode = DebugContext.timer("EmitCode").doc("Time spent generating machine code from LIR.");
+    private static final TimerKey BackEnd = DebugContext.timer("BackEnd").doc("Time spent in EmitLIR and EmitCode.");
 
     /**
      * Encapsulates all the inputs to a {@linkplain GraalCompiler#compile(Request) compilation}.
@@ -167,14 +165,14 @@
      */
     @SuppressWarnings("try")
     public static <T extends CompilationResult> T compile(Request<T> r) {
-        try (Scope s = MethodMetricsRootScopeInfo.createRootScopeIfAbsent(r.installedCodeOwner);
-                        CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(r.graph.getOptions())) {
+        DebugContext debug = r.graph.getDebug();
+        try (CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(r.graph.getOptions())) {
             assert !r.graph.isFrozen();
-            try (Scope s0 = Debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start()) {
+            try (DebugContext.Scope s0 = debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start(debug)) {
                 emitFrontEnd(r.providers, r.backend, r.graph, r.graphBuilderSuite, r.optimisticOpts, r.profilingInfo, r.suites);
                 emitBackEnd(r.graph, null, r.installedCodeOwner, r.backend, r.compilationResult, r.factory, null, r.lirSuites);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
             checkForRequestedCrash(r.graph);
             return r.compilationResult;
@@ -217,32 +215,33 @@
     @SuppressWarnings("try")
     public static void emitFrontEnd(Providers providers, TargetProvider target, StructuredGraph graph, PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts,
                     ProfilingInfo profilingInfo, Suites suites) {
-        try (Scope s = Debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start()) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start(debug)) {
             HighTierContext highTierContext = new HighTierContext(providers, graphBuilderSuite, optimisticOpts);
             if (graph.start().next() == null) {
                 graphBuilderSuite.apply(graph, highTierContext);
                 new DeadCodeEliminationPhase(DeadCodeEliminationPhase.Optionality.Optional).apply(graph);
-                Debug.dump(Debug.BASIC_LEVEL, graph, "After parsing");
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "After parsing");
             } else {
-                Debug.dump(Debug.INFO_LEVEL, graph, "initial state");
+                debug.dump(DebugContext.INFO_LEVEL, graph, "initial state");
             }
 
             suites.getHighTier().apply(graph, highTierContext);
             graph.maybeCompress();
-            Debug.dump(Debug.BASIC_LEVEL, graph, "After high tier");
+            debug.dump(DebugContext.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");
+            debug.dump(DebugContext.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(DebugContext.BASIC_LEVEL, graph, "After low tier");
 
-            Debug.dump(Debug.BASIC_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
+            debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         } finally {
             graph.checkCancellation();
         }
@@ -251,18 +250,19 @@
     @SuppressWarnings("try")
     public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, Backend backend, T compilationResult,
                     CompilationResultBuilderFactory factory, RegisterConfig registerConfig, LIRSuites lirSuites) {
-        try (Scope s = Debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start()) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start(debug)) {
             LIRGenerationResult lirGen = null;
             lirGen = emitLIR(backend, graph, stub, registerConfig, lirSuites);
-            try (Scope s2 = Debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
+            try (DebugContext.Scope s2 = debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
                 int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize();
                 compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess());
                 emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         } finally {
             graph.checkCancellation();
         }
@@ -289,7 +289,8 @@
     @SuppressWarnings("try")
     private static LIRGenerationResult emitLIR0(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites,
                     String[] allocationRestrictedTo) {
-        try (Scope ds = Debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start()) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope ds = debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start(debug)) {
             assert !graph.hasValueProxies();
             ScheduleResult schedule = graph.getLastSchedule();
             Block[] blocks = schedule.getCFG().getBlocks();
@@ -299,7 +300,7 @@
 
             AbstractBlockBase<?>[] codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
             AbstractBlockBase<?>[] linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
-            LIR lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder, graph.getOptions());
+            LIR lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder, graph.getOptions(), graph.getDebug());
 
             FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig);
             LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, graph, stub);
@@ -310,16 +311,16 @@
             LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule);
             new LIRGenerationPhase().apply(backend.getTarget(), lirGenRes, context);
 
-            try (Scope s = Debug.scope("LIRStages", nodeLirGen, lirGenRes, lir)) {
+            try (DebugContext.Scope s = debug.scope("LIRStages", nodeLirGen, lirGenRes, lir)) {
                 // Dump LIR along with HIR (the LIR is looked up from context)
-                Debug.dump(Debug.BASIC_LEVEL, graph.getLastSchedule(), "After LIR generation");
+                debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "After LIR generation");
                 LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig, allocationRestrictedTo));
                 return result;
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         } finally {
             graph.checkCancellation();
         }
@@ -338,17 +339,18 @@
 
     public static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGenerationResult lirGenRes, LIRGeneratorTool lirGen, LIRSuites lirSuites,
                     RegisterAllocationConfig registerAllocationConfig) {
+        DebugContext debug = lirGenRes.getLIR().getDebug();
         PreAllocationOptimizationContext preAllocOptContext = new PreAllocationOptimizationContext(lirGen);
         lirSuites.getPreAllocationOptimizationStage().apply(target, lirGenRes, preAllocOptContext);
-        Debug.dump(Debug.BASIC_LEVEL, lirGenRes.getLIR(), "After PreAllocationOptimizationStage");
+        debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PreAllocationOptimizationStage");
 
         AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory(), registerAllocationConfig);
         lirSuites.getAllocationStage().apply(target, lirGenRes, allocContext);
-        Debug.dump(Debug.BASIC_LEVEL, lirGenRes.getLIR(), "After AllocationStage");
+        debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After AllocationStage");
 
         PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen);
         lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, postAllocOptContext);
-        Debug.dump(Debug.BASIC_LEVEL, lirGenRes.getLIR(), "After PostAllocationOptimizationStage");
+        debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PostAllocationOptimizationStage");
 
         return lirGenRes;
     }
@@ -357,7 +359,8 @@
     public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods, EconomicSet<ResolvedJavaField> accessedFields,
                     int bytecodeSize, LIRGenerationResult lirGenRes,
                     CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) {
-        try (DebugCloseable a = EmitCode.start()) {
+        DebugContext debug = lirGenRes.getLIR().getDebug();
+        try (DebugCloseable a = EmitCode.start(debug)) {
             FrameMap frameMap = lirGenRes.getFrameMap();
             CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory);
             backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner);
@@ -370,12 +373,12 @@
                 compilationResult.setBytecodeSize(bytecodeSize);
             }
             crb.finish();
-            if (Debug.isCountEnabled()) {
+            if (debug.isCountEnabled()) {
                 List<DataPatch> ldp = compilationResult.getDataPatches();
                 JavaKind[] kindValues = JavaKind.values();
-                DebugCounter[] dms = new DebugCounter[kindValues.length];
+                CounterKey[] dms = new CounterKey[kindValues.length];
                 for (int i = 0; i < dms.length; i++) {
-                    dms[i] = Debug.counter("DataPatches-%s", kindValues[i]);
+                    dms[i] = DebugContext.counter("DataPatches-%s", kindValues[i]);
                 }
 
                 for (DataPatch dp : ldp) {
@@ -386,17 +389,17 @@
                             kind = ((JavaConstant) constant).getJavaKind();
                         }
                     }
-                    dms[kind.ordinal()].add(1);
+                    dms[kind.ordinal()].add(debug, 1);
                 }
 
-                Debug.counter("CompilationResults").increment();
-                Debug.counter("CodeBytesEmitted").add(compilationResult.getTargetCodeSize());
-                Debug.counter("InfopointsEmitted").add(compilationResult.getInfopoints().size());
-                Debug.counter("DataPatches").add(ldp.size());
-                Debug.counter("ExceptionHandlersEmitted").add(compilationResult.getExceptionHandlers().size());
+                DebugContext.counter("CompilationResults").increment(debug);
+                DebugContext.counter("CodeBytesEmitted").add(debug, compilationResult.getTargetCodeSize());
+                DebugContext.counter("InfopointsEmitted").add(debug, compilationResult.getInfopoints().size());
+                DebugContext.counter("DataPatches").add(debug, ldp.size());
+                DebugContext.counter("ExceptionHandlersEmitted").add(debug, compilationResult.getExceptionHandlers().size());
             }
 
-            Debug.dump(Debug.BASIC_LEVEL, compilationResult, "After code generation");
+            debug.dump(DebugContext.BASIC_LEVEL, compilationResult, "After code generation");
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalDebugInitializationParticipant.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.core;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Params;
-import org.graalvm.compiler.debug.DebugInitializationParticipant;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.serviceprovider.ServiceProvider;
-
-/**
- * A service provider that may modify the initialization of {@link Debug} based on the values
- * specified for various {@link GraalDebugConfig} options.
- */
-@ServiceProvider(DebugInitializationParticipant.class)
-public class GraalDebugInitializationParticipant implements DebugInitializationParticipant {
-
-    @Override
-    public void apply(Params params) {
-        OptionValues options = params.getOptions();
-        if (GraalDebugConfig.areDebugScopePatternsEnabled(options)) {
-            params.enable = true;
-        }
-        if ("".equals(GraalDebugConfig.Options.Count.getValue(options))) {
-            params.enableUnscopedCounters = true;
-        }
-        if ("".equals(GraalDebugConfig.Options.MethodMeter.getValue(options))) {
-            params.enableUnscopedMethodMetrics = true;
-            // mm requires full debugging support
-            params.enable = true;
-        }
-        if ("".equals(GraalDebugConfig.Options.Time.getValue(options))) {
-            params.enableUnscopedTimers = true;
-        }
-        if ("".equals(GraalDebugConfig.Options.TrackMemUse.getValue(options))) {
-            params.enableUnscopedMemUseTrackers = true;
-        }
-        // unscoped counters/timers/mem use trackers/method metrics should respect method filter
-        // semantics
-        if (!params.enable && (params.enableUnscopedMemUseTrackers || params.enableUnscopedMethodMetrics || params.enableUnscopedCounters || params.enableUnscopedTimers) &&
-                        GraalDebugConfig.isNotEmpty(GraalDebugConfig.Options.MethodFilter, options)) {
-            params.enable = true;
-            params.enableMethodFilter = true;
-        }
-
-        if (!params.enable && GraalDebugConfig.Options.DumpOnPhaseChange.getValue(options) != null) {
-            params.enable = true;
-        }
-
-        if (!params.enableUnscopedMethodMetrics && GraalDebugConfig.Options.MethodMeter.getValue(options) != null) {
-            // mm requires full debugging support
-            params.enable = true;
-        }
-
-        if (GraalDebugConfig.isGlobalMetricsInterceptedByMethodMetricsEnabled(options)) {
-            if (!params.enable) {
-                TTY.println("WARNING: MethodMeter is disabled but GlobalMetricsInterceptedByMethodMetrics is enabled. Ignoring MethodMeter and GlobalMetricsInterceptedByMethodMetrics.");
-            } else {
-                parseMethodMetricsDebugValueInterception(params, options);
-            }
-        }
-        if (GraalDebugConfig.isNotEmpty(GraalDebugConfig.Options.MethodMeter, options) || params.enableUnscopedMethodMetrics) {
-            if (!MethodMetricsPrinter.methodMetricsDumpingEnabled(options)) {
-                TTY.println("WARNING: MethodMeter is enabled but MethodMeter dumping is disabled. Output will not contain MethodMetrics.");
-            }
-        }
-    }
-
-    private static void parseMethodMetricsDebugValueInterception(Params params, OptionValues options) {
-        String interceptionGroup = GraalDebugConfig.Options.GlobalMetricsInterceptedByMethodMetrics.getValue(options);
-        boolean intercepted = false;
-        if (interceptionGroup.contains("Timers")) {
-            params.interceptTime = true;
-            intercepted = true;
-        }
-        if (interceptionGroup.contains("Counters")) {
-            params.interceptCount = true;
-            intercepted = true;
-        }
-        if (interceptionGroup.contains("MemUseTrackers")) {
-            params.interceptMem = true;
-            intercepted = true;
-        }
-
-        if (!intercepted) {
-            TTY.println("WARNING: Ignoring GlobalMetricsInterceptedByMethodMetrics as the supplied argument does not contain Timers/Counters/MemUseTrackers.");
-        }
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/LIRGenerationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/LIRGenerationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,9 +26,10 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.lir.phases.LIRPhase;
@@ -56,8 +57,8 @@
         }
     }
 
-    private static final DebugCounter instructionCounter = Debug.counter("GeneratedLIRInstructions");
-    private static final DebugCounter nodeCount = Debug.counter("FinalNodeCount");
+    private static final CounterKey instructionCounter = DebugContext.counter("GeneratedLIRInstructions");
+    private static final CounterKey nodeCount = DebugContext.counter("FinalNodeCount");
 
     @Override
     protected final void run(TargetDescription target, LIRGenerationResult lirGenRes, LIRGenerationPhase.LIRGenerationContext context) {
@@ -69,18 +70,16 @@
         }
         context.lirGen.beforeRegisterAllocation();
         assert SSAUtil.verifySSAForm(lirGenRes.getLIR());
-        if (nodeCount.isEnabled()) {
-            nodeCount.add(graph.getNodeCount());
-        }
+        nodeCount.add(graph.getDebug(), graph.getNodeCount());
     }
 
     private static void emitBlock(NodeLIRBuilderTool nodeLirGen, LIRGenerationResult lirGenRes, Block b, StructuredGraph graph, BlockMap<List<Node>> blockMap) {
         assert !isProcessed(lirGenRes, b) : "Block already processed " + b;
         assert verifyPredecessors(lirGenRes, b);
         nodeLirGen.doBlock(b, graph, blockMap);
-        if (instructionCounter.isEnabled()) {
-            instructionCounter.add(lirGenRes.getLIR().getLIRforBlock(b).size());
-        }
+        LIR lir = lirGenRes.getLIR();
+        DebugContext debug = lir.getDebug();
+        instructionCounter.add(debug, lir.getLIRforBlock(b).size());
     }
 
     private static boolean verifyPredecessors(LIRGenerationResult lirGenRes, Block block) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,8 @@
 import java.util.Arrays;
 import java.util.Queue;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.ConstantValue;
 import org.graalvm.compiler.lir.LIRFrameState;
@@ -42,8 +42,8 @@
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 import org.graalvm.compiler.virtual.nodes.MaterializedObjectState;
 import org.graalvm.compiler.virtual.nodes.VirtualObjectState;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 
 import jdk.vm.ci.code.BytecodeFrame;
 import jdk.vm.ci.code.VirtualObject;
@@ -61,9 +61,11 @@
 public class DebugInfoBuilder {
 
     protected final NodeValueMap nodeValueMap;
+    protected final DebugContext debug;
 
-    public DebugInfoBuilder(NodeValueMap nodeValueMap) {
+    public DebugInfoBuilder(NodeValueMap nodeValueMap, DebugContext debug) {
         this.nodeValueMap = nodeValueMap;
+        this.debug = debug;
     }
 
     private static final JavaValue[] NO_JAVA_VALUES = {};
@@ -276,10 +278,10 @@
         return toJavaValue(state.lockAt(i));
     }
 
-    private static final DebugCounter STATE_VIRTUAL_OBJECTS = Debug.counter("StateVirtualObjects");
-    private static final DebugCounter STATE_ILLEGALS = Debug.counter("StateIllegals");
-    private static final DebugCounter STATE_VARIABLES = Debug.counter("StateVariables");
-    private static final DebugCounter STATE_CONSTANTS = Debug.counter("StateConstants");
+    private static final CounterKey STATE_VIRTUAL_OBJECTS = DebugContext.counter("StateVirtualObjects");
+    private static final CounterKey STATE_ILLEGALS = DebugContext.counter("StateIllegals");
+    private static final CounterKey STATE_VARIABLES = DebugContext.counter("StateVariables");
+    private static final CounterKey STATE_CONSTANTS = DebugContext.counter("StateConstants");
 
     private static JavaKind toSlotKind(ValueNode value) {
         if (value == null) {
@@ -308,18 +310,18 @@
                         virtualObjects.put(obj, vobject);
                         pendingVirtualObjects.add(obj);
                     }
-                    STATE_VIRTUAL_OBJECTS.increment();
+                    STATE_VIRTUAL_OBJECTS.increment(debug);
                     return vobject;
                 }
             } else {
                 // Remove proxies from constants so the constant can be directly embedded.
                 ValueNode unproxied = GraphUtil.unproxify(value);
                 if (unproxied instanceof ConstantNode) {
-                    STATE_CONSTANTS.increment();
+                    STATE_CONSTANTS.increment(debug);
                     return unproxied.asJavaConstant();
 
                 } else if (value != null) {
-                    STATE_VARIABLES.increment();
+                    STATE_VARIABLES.increment(debug);
                     Value operand = nodeValueMap.operand(value);
                     if (operand instanceof ConstantValue && ((ConstantValue) operand).isJavaConstant()) {
                         return ((ConstantValue) operand).getJavaConstant();
@@ -330,7 +332,7 @@
 
                 } else {
                     // return a dummy value because real value not needed
-                    STATE_ILLEGALS.increment();
+                    STATE_ILLEGALS.increment(debug);
                     return Value.ILLEGAL;
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,12 +26,13 @@
 import static jdk.vm.ci.code.ValueUtil.isLegal;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
 import static org.graalvm.compiler.core.common.GraalOptions.MatchExpressions;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose;
+import static org.graalvm.compiler.debug.DebugOptions.LogVerbose;
 import static org.graalvm.compiler.lir.LIR.verifyBlock;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
@@ -41,8 +42,7 @@
 import org.graalvm.compiler.core.match.MatchPattern;
 import org.graalvm.compiler.core.match.MatchRuleRegistry;
 import org.graalvm.compiler.core.match.MatchStatement;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.GraalGraphError;
@@ -135,7 +135,7 @@
         this.debugInfoBuilder = createDebugInfoBuilder(graph, this);
         OptionValues options = graph.getOptions();
         if (MatchExpressions.getValue(options)) {
-            matchRules = MatchRuleRegistry.lookup(nodeMatchRules.getClass(), options);
+            matchRules = MatchRuleRegistry.lookup(nodeMatchRules.getClass(), options, graph.getDebug());
         }
         traceLIRGeneratorLevel = TTY.isSuppressed() ? 0 : Options.TraceLIRGeneratorLevel.getValue(options);
 
@@ -147,9 +147,8 @@
         return nodeMatchRules;
     }
 
-    @SuppressWarnings({"unused"})
     protected DebugInfoBuilder createDebugInfoBuilder(StructuredGraph graph, NodeValueMap nodeValueMap) {
-        return new DebugInfoBuilder(nodeValueMap);
+        return new DebugInfoBuilder(nodeValueMap, graph.getDebug());
     }
 
     /**
@@ -351,6 +350,7 @@
             for (int i = 0; i < nodes.size(); i++) {
                 Node node = nodes.get(i);
                 if (node instanceof ValueNode) {
+                    DebugContext debug = node.getDebug();
                     ValueNode valueNode = (ValueNode) node;
                     if (trace) {
                         TTY.println("LIRGen for " + valueNode);
@@ -368,9 +368,9 @@
                         }
                     } else if (ComplexMatchValue.INTERIOR_MATCH.equals(operand)) {
                         // Doesn't need to be evaluated
-                        Debug.log("interior match for %s", valueNode);
+                        debug.log("interior match for %s", valueNode);
                     } else if (operand instanceof ComplexMatchValue) {
-                        Debug.log("complex match for %s", valueNode);
+                        debug.log("complex match for %s", valueNode);
                         ComplexMatchValue match = (ComplexMatchValue) operand;
                         operand = match.evaluate(this);
                         if (operand != null) {
@@ -403,11 +403,12 @@
     @SuppressWarnings("try")
     protected void matchComplexExpressions(List<Node> nodes) {
         if (matchRules != null) {
-            try (Scope s = Debug.scope("MatchComplexExpressions")) {
+            DebugContext debug = gen.getResult().getLIR().getDebug();
+            try (DebugContext.Scope s = debug.scope("MatchComplexExpressions")) {
                 if (LogVerbose.getValue(nodeOperands.graph().getOptions())) {
                     int i = 0;
                     for (Node node : nodes) {
-                        Debug.log("%d: (%s) %1S", i++, node.getUsageCount(), node);
+                        debug.log("%d: (%s) %1S", i++, node.getUsageCount(), node);
                     }
                 }
 
@@ -439,15 +440,15 @@
             TTY.println("Emitting LIR for instruction " + instr);
         }
         currentInstruction = instr;
-
-        Debug.log("Visiting %s", instr);
+        DebugContext debug = instr.getDebug();
+        debug.log("Visiting %s", instr);
         emitNode(instr);
-        Debug.log("Operand for %s = %s", instr, getOperand(instr));
+        debug.log("Operand for %s = %s", instr, getOperand(instr));
     }
 
     protected void emitNode(ValueNode node) {
-        if (Debug.isLogEnabled() && node.stamp().isEmpty()) {
-            Debug.log("This node has an empty stamp, we are emitting dead code(?): %s", node);
+        if (node.getDebug().isLogEnabled() && node.stamp().isEmpty()) {
+            node.getDebug().log("This node has an empty stamp, we are emitting dead code(?): %s", node);
         }
         setSourcePosition(node.getNodeSourcePosition());
         if (node instanceof LIRLowerable) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchContext.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchContext.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.core.match;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose;
+import static org.graalvm.compiler.debug.DebugOptions.LogVerbose;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -30,13 +30,13 @@
 
 import org.graalvm.compiler.core.gen.NodeLIRBuilder;
 import org.graalvm.compiler.core.match.MatchPattern.Result;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 
 /**
  * Container for state captured during a match.
@@ -110,10 +110,11 @@
                 continue;
             } else if ((consumed == null || !consumed.contains(node)) && node != root) {
                 if (LogVerbose.getValue(root.getOptions())) {
-                    Debug.log("unexpected node %s", node);
+                    DebugContext debug = root.getDebug();
+                    debug.log("unexpected node %s", node);
                     for (int j = startIndex; j <= endIndex; j++) {
                         Node theNode = nodes.get(j);
-                        Debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.getUsageCount(), theNode);
+                        debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.getUsageCount(), theNode);
                     }
                 }
                 return Result.notSafe(node, rule.getPattern());
@@ -130,9 +131,10 @@
      */
     public void setResult(ComplexMatchResult result) {
         ComplexMatchValue value = new ComplexMatchValue(result);
-        if (Debug.isLogEnabled()) {
-            Debug.log("matched %s %s", rule.getName(), rule.getPattern());
-            Debug.log("with nodes %s", rule.formatMatch(root));
+        DebugContext debug = root.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("matched %s %s", rule.getName(), rule.getPattern());
+            debug.log("with nodes %s", rule.formatMatch(root));
         }
         if (consumed != null) {
             for (Node node : consumed) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchPattern.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchPattern.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,8 @@
  */
 package org.graalvm.compiler.core.match;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.Position;
 import org.graalvm.compiler.nodeinfo.InputType;
@@ -62,12 +62,12 @@
             this.matcher = matcher;
         }
 
-        private static final DebugCounter MatchResult_WRONG_CLASS = Debug.counter("MatchResult_WRONG_CLASS");
-        private static final DebugCounter MatchResult_NAMED_VALUE_MISMATCH = Debug.counter("MatchResult_NAMED_VALUE_MISMATCH");
-        private static final DebugCounter MatchResult_TOO_MANY_USERS = Debug.counter("MatchResult_TOO_MANY_USERS");
-        private static final DebugCounter MatchResult_NOT_IN_BLOCK = Debug.counter("MatchResult_NOT_IN_BLOCK");
-        private static final DebugCounter MatchResult_NOT_SAFE = Debug.counter("MatchResult_NOT_SAFE");
-        private static final DebugCounter MatchResult_ALREADY_USED = Debug.counter("MatchResult_ALREADY_USED");
+        private static final CounterKey MatchResult_WRONG_CLASS = DebugContext.counter("MatchResult_WRONG_CLASS");
+        private static final CounterKey MatchResult_NAMED_VALUE_MISMATCH = DebugContext.counter("MatchResult_NAMED_VALUE_MISMATCH");
+        private static final CounterKey MatchResult_TOO_MANY_USERS = DebugContext.counter("MatchResult_TOO_MANY_USERS");
+        private static final CounterKey MatchResult_NOT_IN_BLOCK = DebugContext.counter("MatchResult_NOT_IN_BLOCK");
+        private static final CounterKey MatchResult_NOT_SAFE = DebugContext.counter("MatchResult_NOT_SAFE");
+        private static final CounterKey MatchResult_ALREADY_USED = DebugContext.counter("MatchResult_ALREADY_USED");
 
         static final Result OK = new Result(MatchResultCode.OK, null, null);
         private static final Result CACHED_WRONG_CLASS = new Result(MatchResultCode.WRONG_CLASS, null, null);
@@ -78,33 +78,33 @@
         private static final Result CACHED_ALREADY_USED = new Result(MatchResultCode.ALREADY_USED, null, null);
 
         static Result wrongClass(Node node, MatchPattern matcher) {
-            MatchResult_WRONG_CLASS.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.WRONG_CLASS, node, matcher) : CACHED_WRONG_CLASS;
+            MatchResult_WRONG_CLASS.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.WRONG_CLASS, node, matcher) : CACHED_WRONG_CLASS;
         }
 
         static Result namedValueMismatch(Node node, MatchPattern matcher) {
-            MatchResult_NAMED_VALUE_MISMATCH.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.NAMED_VALUE_MISMATCH, node, matcher) : CACHED_NAMED_VALUE_MISMATCH;
+            MatchResult_NAMED_VALUE_MISMATCH.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.NAMED_VALUE_MISMATCH, node, matcher) : CACHED_NAMED_VALUE_MISMATCH;
         }
 
         static Result tooManyUsers(Node node, MatchPattern matcher) {
-            MatchResult_TOO_MANY_USERS.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.TOO_MANY_USERS, node, matcher) : CACHED_TOO_MANY_USERS;
+            MatchResult_TOO_MANY_USERS.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.TOO_MANY_USERS, node, matcher) : CACHED_TOO_MANY_USERS;
         }
 
         static Result notInBlock(Node node, MatchPattern matcher) {
-            MatchResult_NOT_IN_BLOCK.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_IN_BLOCK, node, matcher) : CACHED_NOT_IN_BLOCK;
+            MatchResult_NOT_IN_BLOCK.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.NOT_IN_BLOCK, node, matcher) : CACHED_NOT_IN_BLOCK;
         }
 
         static Result notSafe(Node node, MatchPattern matcher) {
-            MatchResult_NOT_SAFE.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_SAFE, node, matcher) : CACHED_NOT_SAFE;
+            MatchResult_NOT_SAFE.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.NOT_SAFE, node, matcher) : CACHED_NOT_SAFE;
         }
 
         static Result alreadyUsed(Node node, MatchPattern matcher) {
-            MatchResult_ALREADY_USED.increment();
-            return Debug.isLogEnabled() ? new Result(MatchResultCode.ALREADY_USED, node, matcher) : CACHED_ALREADY_USED;
+            MatchResult_ALREADY_USED.increment(node.getDebug());
+            return node.getDebug().isLogEnabled() ? new Result(MatchResultCode.ALREADY_USED, node, matcher) : CACHED_ALREADY_USED;
         }
 
         @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchRuleRegistry.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchRuleRegistry.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,13 @@
  */
 package org.graalvm.compiler.core.match;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose;
+import static org.graalvm.compiler.debug.DebugOptions.LogVerbose;
+
 import java.util.ArrayList;
 import java.util.List;
 
 import org.graalvm.compiler.core.gen.NodeMatchRules;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Edges;
 import org.graalvm.compiler.graph.Node;
@@ -36,8 +36,8 @@
 import org.graalvm.compiler.graph.Position;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.serviceprovider.GraalServices;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 import org.graalvm.util.MapCursor;
 
 public class MatchRuleRegistry {
@@ -77,7 +77,7 @@
      * @return the set of {@link MatchStatement}s applicable to theClass.
      */
     @SuppressWarnings("try")
-    public static synchronized EconomicMap<Class<? extends Node>, List<MatchStatement>> lookup(Class<? extends NodeMatchRules> theClass, OptionValues options) {
+    public static synchronized EconomicMap<Class<? extends Node>, List<MatchStatement>> lookup(Class<? extends NodeMatchRules> theClass, OptionValues options, DebugContext debug) {
         EconomicMap<Class<? extends Node>, List<MatchStatement>> result = registry.get(theClass);
 
         if (result == null) {
@@ -87,13 +87,13 @@
             result = rules;
 
             if (LogVerbose.getValue(options)) {
-                try (Scope s = Debug.scope("MatchComplexExpressions")) {
-                    Debug.log("Match rules for %s", theClass.getSimpleName());
+                try (DebugContext.Scope s = debug.scope("MatchComplexExpressions")) {
+                    debug.log("Match rules for %s", theClass.getSimpleName());
                     MapCursor<Class<? extends Node>, List<MatchStatement>> cursor = result.getEntries();
                     while (cursor.advance()) {
-                        Debug.log("  For node class: %s", cursor.getKey());
+                        debug.log("  For node class: %s", cursor.getKey());
                         for (MatchStatement statement : cursor.getValue()) {
-                            Debug.log("    %s", statement.getPattern());
+                            debug.log("    %s", statement.getPattern());
                         }
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchStatement.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchStatement.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,28 +22,28 @@
  */
 package org.graalvm.compiler.core.match;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose;
+import static org.graalvm.compiler.debug.DebugOptions.LogVerbose;
 
 import java.util.List;
 
-import jdk.vm.ci.meta.Value;
-
 import org.graalvm.compiler.core.gen.NodeLIRBuilder;
 import org.graalvm.compiler.core.match.MatchPattern.MatchResultCode;
 import org.graalvm.compiler.core.match.MatchPattern.Result;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodeinfo.Verbosity;
 
+import jdk.vm.ci.meta.Value;
+
 /**
  * A named {@link MatchPattern} along with a {@link MatchGenerator} that can be evaluated to replace
  * one or more {@link Node}s with a single {@link Value}.
  */
 
 public class MatchStatement {
-    private static final DebugCounter MatchStatementSuccess = Debug.counter("MatchStatementSuccess");
+    private static final CounterKey MatchStatementSuccess = DebugContext.counter("MatchStatementSuccess");
 
     /**
      * A printable name for this statement. Usually it's just the name of the method doing the
@@ -83,6 +83,7 @@
      *         evaluated by the NodeLIRBuilder.
      */
     public boolean generate(NodeLIRBuilder builder, int index, Node node, List<Node> nodes) {
+        DebugContext debug = node.getDebug();
         assert index == nodes.indexOf(node);
         // Check that the basic shape matches
         Result result = pattern.matchShape(node, this);
@@ -97,19 +98,19 @@
             ComplexMatchResult value = generatorMethod.match(builder.getNodeMatchRules(), buildArgList(context));
             if (value != null) {
                 context.setResult(value);
-                MatchStatementSuccess.increment();
-                Debug.counter("MatchStatement[%s]", getName()).increment();
+                MatchStatementSuccess.increment(debug);
+                DebugContext.counter("MatchStatement[%s]", getName()).increment(debug);
                 return true;
             }
             // The pattern matched but some other code generation constraint disallowed code
             // generation for the pattern.
             if (LogVerbose.getValue(node.getOptions())) {
-                Debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName());
-                Debug.log("with nodes %s", formatMatch(node));
+                debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName());
+                debug.log("with nodes %s", formatMatch(node));
             }
         } else {
             if (LogVerbose.getValue(node.getOptions()) && result.code != MatchResultCode.WRONG_CLASS) {
-                Debug.log("while matching %s|%s %s %s", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), result);
+                debug.log("while matching %s|%s %s %s", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), result);
             }
         }
         return false;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyCompilerConfiguration.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyCompilerConfiguration.java	Fri Jul 07 09:40:47 2017 -0700
@@ -60,7 +60,7 @@
 
     @Override
     public LIRPhaseSuite<AllocationContext> createAllocationStage(OptionValues options) {
-        return new EconomyAllocationStage();
+        return new EconomyAllocationStage(options);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,7 @@
  */
 package org.graalvm.compiler.core.phases;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph.NodeEvent;
 import org.graalvm.compiler.graph.Graph.NodeEventScope;
 import org.graalvm.compiler.graph.Node;
@@ -33,8 +32,8 @@
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicSet;
 
 /**
  * A utility phase for detecting when a phase would change the graph and reporting extra information
@@ -68,12 +67,13 @@
          * having their inputs change are the main interesting differences.
          */
         HashSetNodeEventListener listener = new HashSetNodeEventListener().exclude(NodeEvent.NODE_ADDED);
-        StructuredGraph graphCopy = (StructuredGraph) graph.copy();
+        StructuredGraph graphCopy = (StructuredGraph) graph.copy(graph.getDebug());
+        DebugContext debug = graph.getDebug();
         try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) {
-            try (Scope s2 = Debug.sandbox("WithoutMonitoring", null)) {
+            try (DebugContext.Scope s2 = debug.sandbox("WithoutMonitoring", null)) {
                 super.run(graphCopy, context);
             } catch (Throwable t) {
-                Debug.handle(t);
+                debug.handle(t);
             }
         }
 
@@ -90,15 +90,15 @@
             /* rerun it on the real graph in a new Debug scope so Dump and Log can find it. */
             listener = new HashSetNodeEventListener();
             try (NodeEventScope s = graph.trackNodeEvents(listener)) {
-                try (Scope s2 = Debug.scope("WithGraphChangeMonitoring")) {
-                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** Before phase %s", getName());
+                try (DebugContext.Scope s2 = debug.scope("WithGraphChangeMonitoring")) {
+                    if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "*** Before phase %s", getName());
                     }
                     super.run(graph, context);
-                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** After phase %s %s", getName(), filteredNodes);
+                    if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "*** After phase %s %s", getName(), filteredNodes);
                     }
-                    Debug.log("*** %s %s %s\n", message, graph, filteredNodes);
+                    debug.log("*** %s %s %s\n", message, graph, filteredNodes);
                 }
             }
         } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/MidTier.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/MidTier.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,9 +26,14 @@
 import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
 import static org.graalvm.compiler.core.common.GraalOptions.OptDeoptimizationGrouping;
 import static org.graalvm.compiler.core.common.GraalOptions.OptFloatingReads;
+import static org.graalvm.compiler.core.common.GraalOptions.OptLoopTransform;
+import static org.graalvm.compiler.core.common.GraalOptions.PartialUnroll;
 import static org.graalvm.compiler.core.common.GraalOptions.ReassociateInvariants;
 import static org.graalvm.compiler.core.common.GraalOptions.VerifyHeapAtReturn;
 
+import org.graalvm.compiler.loop.DefaultLoopPolicies;
+import org.graalvm.compiler.loop.LoopPolicies;
+import org.graalvm.compiler.loop.phases.LoopPartialUnrollPhase;
 import org.graalvm.compiler.loop.phases.LoopSafepointEliminationPhase;
 import org.graalvm.compiler.loop.phases.ReassociateInvariantPhase;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -79,6 +84,12 @@
 
         appendPhase(new FrameStateAssignmentPhase());
 
+        LoopPolicies loopPolicies = createLoopPolicies();
+        if (OptLoopTransform.getValue(options)) {
+            if (PartialUnroll.getValue(options)) {
+                appendPhase(new LoopPartialUnrollPhase(loopPolicies, canonicalizer));
+            }
+        }
         if (ReassociateInvariants.getValue(options)) {
             appendPhase(new ReassociateInvariantPhase());
         }
@@ -89,4 +100,8 @@
 
         appendPhase(canonicalizer);
     }
+
+    public LoopPolicies createLoopPolicies() {
+        return new DefaultLoopPolicies();
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,8 @@
  */
 package org.graalvm.compiler.core.target;
 
+import java.util.ArrayList;
+
 import org.graalvm.compiler.asm.Assembler;
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
@@ -29,8 +31,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
@@ -60,8 +61,6 @@
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.SpeculationLog;
 
-import java.util.ArrayList;
-
 /**
  * Represents a compiler backend for Graal.
  */
@@ -155,22 +154,22 @@
     protected abstract CompiledCode createCompiledCode(ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult);
 
     /**
-     * @see #createInstalledCode(ResolvedJavaMethod, CompilationRequest, CompilationResult,
-     *      SpeculationLog, InstalledCode, boolean)
+     * @see #createInstalledCode(DebugContext, ResolvedJavaMethod, CompilationRequest,
+     *      CompilationResult, SpeculationLog, InstalledCode, boolean, Object[])
      */
-    public InstalledCode createInstalledCode(ResolvedJavaMethod method, CompilationResult compilationResult,
+    public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationResult compilationResult,
                     SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault) {
-        return createInstalledCode(method, null, compilationResult, speculationLog, predefinedInstalledCode, isDefault);
+        return createInstalledCode(debug, method, null, compilationResult, speculationLog, predefinedInstalledCode, isDefault, null);
     }
 
     /**
-     * @see #createInstalledCode(ResolvedJavaMethod, CompilationRequest, CompilationResult,
-     *      SpeculationLog, InstalledCode, boolean, Object[])
+     * @see #createInstalledCode(DebugContext, ResolvedJavaMethod, CompilationRequest,
+     *      CompilationResult, SpeculationLog, InstalledCode, boolean, Object[])
      */
     @SuppressWarnings("try")
-    public InstalledCode createInstalledCode(ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult,
+    public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult,
                     SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault) {
-        return createInstalledCode(method, compilationRequest, compilationResult, speculationLog, predefinedInstalledCode, isDefault, null);
+        return createInstalledCode(debug, method, compilationRequest, compilationResult, speculationLog, predefinedInstalledCode, isDefault, null);
     }
 
     /**
@@ -188,19 +187,20 @@
      *            {@code compRequest.getMethod()}. The default implementation for a method is the
      *            code executed for standard calls to the method. This argument is ignored if
      *            {@code compRequest == null}.
-     * @param context a custom debug context to use for the code installation.
+     * @param context a custom debug context to use for the code installation
      * @return a reference to the compiled and ready-to-run installed code
      * @throws BailoutException if the code installation failed
      */
     @SuppressWarnings("try")
-    public InstalledCode createInstalledCode(ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult,
+    public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult,
                     SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault, Object[] context) {
         Object[] debugContext = context != null ? context : new Object[]{getProviders().getCodeCache(), method, compilationResult};
         CodeInstallationTask[] tasks = new CodeInstallationTask[codeInstallationTaskFactories.size()];
         for (int i = 0; i < codeInstallationTaskFactories.size(); i++) {
             tasks[i] = codeInstallationTaskFactories.get(i).create();
         }
-        try (Scope s = Debug.scope("CodeInstall", debugContext)) {
+        try (DebugContext.Scope s2 = debug.scope("CodeInstall", debugContext);
+                        DebugContext.Activation a = debug.activate()) {
             for (CodeInstallationTask task : tasks) {
                 task.preProcess(compilationResult);
             }
@@ -222,7 +222,7 @@
             }
             return installedCode;
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -236,8 +236,8 @@
      * @return a reference to the compiled and ready-to-run installed code
      * @throws BailoutException if the code installation failed
      */
-    public InstalledCode addInstalledCode(ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult) {
-        return createInstalledCode(method, compilationRequest, compilationResult, null, null, false);
+    public InstalledCode addInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult) {
+        return createInstalledCode(debug, method, compilationRequest, compilationResult, null, null, false);
     }
 
     /**
@@ -250,8 +250,8 @@
      * @return a reference to the compiled and ready-to-run installed code
      * @throws BailoutException if the code installation failed
      */
-    public InstalledCode createDefaultInstalledCode(ResolvedJavaMethod method, CompilationResult compilationResult) {
-        return createInstalledCode(method, compilationResult, null, null, true);
+    public InstalledCode createDefaultInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationResult compilationResult) {
+        return createInstalledCode(debug, method, compilationResult, null, null, true);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug.test;
+
+import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION;
+import static org.graalvm.compiler.debug.DebugContext.NO_GLOBAL_METRIC_VALUES;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.List;
+
+import org.graalvm.compiler.debug.Assertions;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
+import org.graalvm.compiler.debug.DebugDumpHandler;
+import org.graalvm.compiler.debug.DebugHandler;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.debug.DebugVerifyHandler;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+@SuppressWarnings("try")
+public class DebugContextTest {
+
+    static class DebugContextSetup {
+        final Formatter dumpOutput = new Formatter();
+        final Formatter verifyOutput = new Formatter();
+        final ByteArrayOutputStream logOutput = new ByteArrayOutputStream();
+        DebugHandlersFactory handlers = new DebugHandlersFactory() {
+            @Override
+            public List<DebugHandler> createHandlers(OptionValues options) {
+                return Arrays.asList(new DebugDumpHandler() {
+                    @Override
+                    public void dump(DebugContext ignore, Object object, String format, Object... arguments) {
+                        dumpOutput.format("Dumping %s with label \"%s\"%n", object, String.format(format, arguments));
+                    }
+                }, new DebugVerifyHandler() {
+                    @Override
+                    public void verify(DebugContext ignore, Object object, String format, Object... args) {
+                        verifyOutput.format("Verifying %s with label \"%s\"%n", object, String.format(format, args));
+                    }
+                });
+            }
+        };
+
+        DebugContext openDebugContext(OptionValues options) {
+            return DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(logOutput), Collections.singletonList(handlers));
+
+        }
+    }
+
+    @Test
+    public void testDisabledScopes() {
+        OptionValues options = new OptionValues(EconomicMap.create());
+        DebugContextSetup setup = new DebugContextSetup();
+        try (DebugContext debug = setup.openDebugContext(options);
+                        DebugContext.Scope d = debug.scope("TestDisabledScoping")) {
+            for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
+                debug.dump(level, "an object", "at level %d", level);
+                debug.verify("an object", "at level %d", level);
+                debug.log(level, "log statement at level %d", level);
+            }
+        }
+        String log = setup.logOutput.toString();
+        String dumpOutput = setup.dumpOutput.toString();
+        String verifyOutput = setup.verifyOutput.toString();
+        Assert.assertTrue(log, log.isEmpty());
+        Assert.assertTrue(dumpOutput, dumpOutput.isEmpty());
+        Assert.assertTrue(verifyOutput, verifyOutput.isEmpty());
+    }
+
+    @Test
+    public void testDumping() {
+        for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
+            OptionValues options = new OptionValues(EconomicMap.create());
+            options = new OptionValues(options, DebugOptions.Dump, "Scope" + level + ":" + level);
+            DebugContextSetup setup = new DebugContextSetup();
+            try (DebugContext debug = setup.openDebugContext(options);
+                            DebugContext.Scope s0 = debug.scope("TestDumping")) {
+                try (DebugContext.Scope s1 = debug.scope("Scope1")) {
+                    try (DebugContext.Scope s2 = debug.scope("Scope2")) {
+                        try (DebugContext.Scope s3 = debug.scope("Scope3")) {
+                            try (DebugContext.Scope s4 = debug.scope("Scope4")) {
+                                try (DebugContext.Scope s5 = debug.scope("Scope5")) {
+                                    debug.dump(level, "an object", "at level %d", level);
+                                }
+                            }
+                        }
+                    }
+                }
+
+            }
+
+            String expect = String.format("Dumping an object with label \"at level %d\"%n", level);
+            String dump = setup.dumpOutput.toString();
+            Assert.assertEquals(expect, dump);
+        }
+    }
+
+    @Test
+    public void testLogging() throws IOException {
+        OptionValues options = new OptionValues(EconomicMap.create());
+        options = new OptionValues(options, DebugOptions.Log, ":5");
+        DebugContextSetup setup = new DebugContextSetup();
+        try (DebugContext debug = setup.openDebugContext(options)) {
+            for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
+                try (DebugContext.Scope s0 = debug.scope("TestLogging")) {
+                    debug.log(level, "log statement at level %d", level);
+                    try (DebugContext.Scope s1 = debug.scope("Level1")) {
+                        debug.log(level, "log statement at level %d", level);
+                        try (DebugContext.Scope s2 = debug.scope("Level2")) {
+                            debug.log(level, "log statement at level %d", level);
+                            try (DebugContext.Scope s3 = debug.scope("Level3")) {
+                                debug.log(level, "log statement at level %d", level);
+                                try (DebugContext.Scope s4 = debug.scope("Level4")) {
+                                    debug.log(level, "log statement at level %d", level);
+                                    try (DebugContext.Scope s5 = debug.scope("Level5")) {
+                                        debug.log(level, "log statement at level %d", level);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        DataInputStream in = new DataInputStream(getClass().getResourceAsStream(getClass().getSimpleName() + ".testLogging.input"));
+        byte[] buf = new byte[in.available()];
+        in.readFully(buf);
+        String threadLabel = "[thread:" + Thread.currentThread().getId() + "]";
+        String expect = new String(buf).replace("[thread:1]", threadLabel);
+
+        String log = setup.logOutput.toString();
+        Assert.assertEquals(expect, log);
+    }
+
+    @Test
+    public void testEnabledSandbox() {
+        EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
+        // Configure with an option that enables scopes
+        map.put(DebugOptions.DumpOnError, true);
+        OptionValues options = new OptionValues(map);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
+        Exception e = new Exception("testEnabledSandbox");
+        String scopeName = "";
+        try {
+            try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", debug.getConfig())) {
+                scopeName = d.getQualifiedName();
+                throw e;
+            } catch (Throwable t) {
+                assert e == t;
+                debug.handle(t);
+            }
+        } catch (Throwable t) {
+            // The exception object should propagate all the way out through
+            // a enabled sandbox scope
+            Assert.assertEquals(e, t);
+        }
+        String logged = baos.toString();
+        String expected = String.format("Exception raised in scope %s: %s", scopeName, e);
+        String line = "-------------------------------------------------------";
+        Assert.assertTrue(String.format("Could not find \"%s\" in content between lines below:%n%s%n%s%s", expected, line, logged, line), logged.contains(expected));
+    }
+
+    @Test
+    public void testDisabledSandbox() {
+        EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
+        // Configure with an option that enables scopes
+        map.put(DebugOptions.DumpOnError, true);
+        OptionValues options = new OptionValues(map);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
+        Exception e = new Exception("testDisabledSandbox");
+        try {
+            // Test a disabled sandbox scope
+            try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", null)) {
+                throw e;
+            } catch (Throwable t) {
+                assert e == t;
+                debug.handle(t);
+            }
+        } catch (Throwable t) {
+            // The exception object should propagate all the way out through
+            // a disabled sandbox scope
+            Assert.assertEquals(e, t);
+        }
+        String logged = baos.toString();
+        Assert.assertTrue(logged, logged.isEmpty());
+    }
+
+    /**
+     * Tests that using a {@link DebugContext} on a thread other than the one on which it was
+     * created causes an assertion failure.
+     */
+    @Test
+    public void testInvariantChecking() throws InterruptedException {
+        Assume.assumeTrue(Assertions.ENABLED);
+        EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
+        // Configure with an option that enables counters
+        map.put(DebugOptions.Counters, "");
+        OptionValues options = new OptionValues(map);
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+        CounterKey counter = DebugContext.counter("DebugContextTestCounter");
+        AssertionError[] result = {null};
+        Thread thread = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    counter.add(debug, 1);
+                } catch (AssertionError e) {
+                    result[0] = e;
+                }
+            }
+        };
+        thread.start();
+        thread.join();
+
+        Assert.assertNotNull("Expected thread to throw AssertionError", result[0]);
+    }
+
+    @Test
+    public void testDisableIntercept() {
+        EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
+        // Configure with an option that enables scopes
+        map.put(DebugOptions.DumpOnError, true);
+        OptionValues options = new OptionValues(map);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
+        Exception e = new Exception();
+        try {
+            try (DebugCloseable disabled = debug.disableIntercept(); Scope s1 = debug.scope("ScopeWithDisabledIntercept")) {
+                try (Scope s2 = debug.scope("InnerScopeInheritsDisabledIntercept")) {
+                    throw e;
+                }
+            } catch (Throwable t) {
+                assert e == t;
+                debug.handle(t);
+            }
+        } catch (Throwable t) {
+            // The exception object should propagate all the way out through
+            // an intercept disabled scope
+            Assert.assertEquals(e, t);
+        }
+        String logged = baos.toString();
+        Assert.assertEquals("Exception should not have been intercepted", "", logged);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.testLogging.input	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,61 @@
+[thread:1] scope: main
+  [thread:1] scope: main.TestLogging
+  log statement at level 1
+    [thread:1] scope: main.TestLogging.Level1
+    log statement at level 1
+      [thread:1] scope: main.TestLogging.Level1.Level2
+      log statement at level 1
+        [thread:1] scope: main.TestLogging.Level1.Level2.Level3
+        log statement at level 1
+          [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4
+          log statement at level 1
+            [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4.Level5
+            log statement at level 1
+  [thread:1] scope: main.TestLogging
+  log statement at level 2
+    [thread:1] scope: main.TestLogging.Level1
+    log statement at level 2
+      [thread:1] scope: main.TestLogging.Level1.Level2
+      log statement at level 2
+        [thread:1] scope: main.TestLogging.Level1.Level2.Level3
+        log statement at level 2
+          [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4
+          log statement at level 2
+            [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4.Level5
+            log statement at level 2
+  [thread:1] scope: main.TestLogging
+  log statement at level 3
+    [thread:1] scope: main.TestLogging.Level1
+    log statement at level 3
+      [thread:1] scope: main.TestLogging.Level1.Level2
+      log statement at level 3
+        [thread:1] scope: main.TestLogging.Level1.Level2.Level3
+        log statement at level 3
+          [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4
+          log statement at level 3
+            [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4.Level5
+            log statement at level 3
+  [thread:1] scope: main.TestLogging
+  log statement at level 4
+    [thread:1] scope: main.TestLogging.Level1
+    log statement at level 4
+      [thread:1] scope: main.TestLogging.Level1.Level2
+      log statement at level 4
+        [thread:1] scope: main.TestLogging.Level1.Level2.Level3
+        log statement at level 4
+          [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4
+          log statement at level 4
+            [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4.Level5
+            log statement at level 4
+  [thread:1] scope: main.TestLogging
+  log statement at level 5
+    [thread:1] scope: main.TestLogging.Level1
+    log statement at level 5
+      [thread:1] scope: main.TestLogging.Level1.Level2
+      log statement at level 5
+        [thread:1] scope: main.TestLogging.Level1.Level2.Level3
+        log statement at level 5
+          [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4
+          log statement at level 5
+            [thread:1] scope: main.TestLogging.Level1.Level2.Level3.Level4.Level5
+            log statement at level 5
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugHistogramTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2013, 2013, 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.test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugHistogram;
-import org.graalvm.compiler.debug.internal.DebugHistogramAsciiPrinter;
-import org.graalvm.compiler.debug.internal.DebugHistogramRPrinter;
-
-public class DebugHistogramTest {
-
-    @Test
-    public void testEmptyHistogram() {
-        DebugHistogram histogram = Debug.createHistogram("TestHistogram");
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-
-        new DebugHistogramAsciiPrinter(new PrintStream(outputStream)).print(histogram);
-        String line = outputStream.toString().split("\r?\n")[0];
-        Assert.assertEquals("TestHistogram is empty.", line);
-
-        outputStream.reset();
-        new DebugHistogramRPrinter(new PrintStream(outputStream)).print(histogram);
-        Assert.assertEquals("", outputStream.toString());
-    }
-
-    @Test
-    public void testSingleEntryHistogram() {
-        DebugHistogram histogram = Debug.createHistogram("TestHistogram");
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        histogram.add(new Integer(1));
-        histogram.add(new Integer(1));
-        new DebugHistogramAsciiPrinter(new PrintStream(outputStream)).print(histogram);
-        String[] lines = outputStream.toString().split("\r?\n");
-        // @formatter:off
-        String[] expected = {
-            "TestHistogram has 1 unique elements and 2 total elements:",
-            "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------",
-            "| 1                                                  | 2          | ==================================================================================================== |",
-            "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
-        };
-        // @formatter:on
-        Assert.assertArrayEquals(expected, lines);
-
-        outputStream.reset();
-        new DebugHistogramRPrinter(new PrintStream(outputStream)).print(histogram);
-        lines = outputStream.toString().split("\r?\n");
-        // @formatter:off
-        expected = new String[] {
-            "TestHistogram <- c(2);",
-            "names(TestHistogram) <- c(\"1\");"
-        };
-        // @formatter:on
-        Assert.assertArrayEquals(expected, lines);
-    }
-
-    @Test
-    public void testMultipleEntryHistogram() {
-        DebugHistogram histogram = Debug.createHistogram("TestHistogram");
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        histogram.add(new Integer(1));
-        histogram.add(new Integer(2));
-        histogram.add(new Integer(2));
-        new DebugHistogramAsciiPrinter(new PrintStream(outputStream)).print(histogram);
-        String[] lines = outputStream.toString().split("\r?\n");
-        // @formatter:off
-        String[] expected = new String[] {
-            "TestHistogram has 2 unique elements and 3 total elements:",
-            "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------",
-            "| 2                                                  | 2          | ==================================================================================================== |",
-            "| 1                                                  | 1          | ==================================================                                                   |",
-            "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
-        };
-        // @formatter:on
-        Assert.assertArrayEquals(expected, lines);
-
-        outputStream.reset();
-        new DebugHistogramRPrinter(new PrintStream(outputStream)).print(histogram);
-        lines = outputStream.toString().split("\r?\n");
-        // @formatter:off
-        expected = new String[] {
-            "TestHistogram <- c(2, 1);",
-            "names(TestHistogram) <- c(\"2\", \"1\");"
-        };
-        // @formatter:on
-        Assert.assertArrayEquals(expected, lines);
-    }
-
-    @Test
-    public void testTooLongValueString() {
-        DebugHistogram histogram = Debug.createHistogram("TestHistogram");
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        histogram.add("MyCustomValue");
-        new DebugHistogramAsciiPrinter(new PrintStream(outputStream), Integer.MAX_VALUE, 10, 10, 1).print(histogram);
-        String[] lines = outputStream.toString().split("\r?\n");
-        Assert.assertEquals(4, lines.length);
-        Assert.assertEquals("TestHistogram has 1 unique elements and 1 total elements:", lines[0]);
-        Assert.assertEquals("----------------------------------------", lines[1]);
-        Assert.assertEquals("| MyCusto... | 1          | ========== |", lines[2]);
-        Assert.assertEquals("----------------------------------------", lines[3]);
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugTimerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.test;
-
-import static org.junit.Assert.assertEquals;
-
-import java.lang.management.ThreadMXBean;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.Management;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.util.EconomicMap;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-
-@SuppressWarnings("try")
-public class DebugTimerTest {
-
-    private static final ThreadMXBean threadMXBean = Management.getThreadMXBean();
-
-    @Before
-    public void checkCapabilities() {
-        Assume.assumeTrue("skipping management interface test", threadMXBean.isCurrentThreadCpuTimeSupported());
-    }
-
-    /**
-     * Actively spins the current thread for at least a given number of milliseconds in such a way
-     * that timers for the current thread keep ticking over.
-     *
-     * @return the number of milliseconds actually spent spinning which is guaranteed to be >=
-     *         {@code ms}
-     */
-    private static long spin(long ms) {
-        long start = threadMXBean.getCurrentThreadCpuTime();
-        do {
-            long durationMS = (threadMXBean.getCurrentThreadCpuTime() - start) / 1000;
-            if (durationMS >= ms) {
-                return durationMS;
-            }
-        } while (true);
-    }
-
-    /**
-     * Asserts that a timer replied recursively without any other interleaving timers has the same
-     * flat and accumulated times.
-     */
-    @Test
-    public void test2() {
-        OptionValues options = new OptionValues(EconomicMap.create());
-        DebugConfig debugConfig = Debug.fixedConfig(options, 0, 0, false, false, true, false, false, null, null, System.out);
-        try (DebugConfigScope dcs = new DebugConfigScope(debugConfig); Debug.Scope s = Debug.scope("DebugTimerTest")) {
-            DebugTimer timerC = Debug.timer("TimerC");
-            try (DebugCloseable c1 = timerC.start()) {
-                spin(50);
-                try (DebugCloseable c2 = timerC.start()) {
-                    spin(50);
-                    try (DebugCloseable c3 = timerC.start()) {
-                        spin(50);
-                        try (DebugCloseable c4 = timerC.start()) {
-                            spin(50);
-                            try (DebugCloseable c5 = timerC.start()) {
-                                spin(50);
-                            }
-                        }
-                    }
-                }
-            }
-            if (timerC.getFlat() != null) {
-                assertEquals(timerC.getFlat().getCurrentValue(), timerC.getCurrentValue());
-            }
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/TimerKeyTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug.test;
+
+import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
+import static org.graalvm.compiler.debug.DebugContext.NO_CONFIG_CUSTOMIZERS;
+import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION;
+import static org.graalvm.compiler.debug.DebugContext.NO_GLOBAL_METRIC_VALUES;
+import static org.junit.Assert.assertEquals;
+
+import java.lang.management.ThreadMXBean;
+
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.debug.Management;
+import org.graalvm.compiler.debug.TimerKey;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("try")
+public class TimerKeyTest {
+
+    private static final ThreadMXBean threadMXBean = Management.getThreadMXBean();
+
+    @Before
+    public void checkCapabilities() {
+        Assume.assumeTrue("skipping management interface test", threadMXBean.isCurrentThreadCpuTimeSupported());
+    }
+
+    /**
+     * Actively spins the current thread for at least a given number of milliseconds in such a way
+     * that timers for the current thread keep ticking over.
+     *
+     * @return the number of milliseconds actually spent spinning which is guaranteed to be >=
+     *         {@code ms}
+     */
+    private static long spin(long ms) {
+        long start = threadMXBean.getCurrentThreadCpuTime();
+        do {
+            long durationMS = (threadMXBean.getCurrentThreadCpuTime() - start) / 1000;
+            if (durationMS >= ms) {
+                return durationMS;
+            }
+        } while (true);
+    }
+
+    /**
+     * Asserts that a timer replied recursively without any other interleaving timers has the same
+     * flat and accumulated times.
+     */
+    @Test
+    public void test2() {
+        EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
+        map.put(DebugOptions.Time, "");
+        OptionValues options = new OptionValues(map);
+        DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, NO_CONFIG_CUSTOMIZERS);
+
+        TimerKey timerC = DebugContext.timer("TimerC");
+        try (DebugCloseable c1 = timerC.start(debug)) {
+            spin(50);
+            try (DebugCloseable c2 = timerC.start(debug)) {
+                spin(50);
+                try (DebugCloseable c3 = timerC.start(debug)) {
+                    spin(50);
+                    try (DebugCloseable c4 = timerC.start(debug)) {
+                        spin(50);
+                        try (DebugCloseable c5 = timerC.start(debug)) {
+                            spin(50);
+                        }
+                    }
+                }
+            }
+        }
+        if (timerC.getFlat() != null) {
+            assertEquals(timerC.getFlat().getCurrentValue(debug), timerC.getCurrentValue(debug));
+        }
+    }
+}
--- /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/AbstractKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+/**
+ * A name and index for a metric value.
+ */
+abstract class AbstractKey implements MetricKey {
+
+    private final String nameFormat;
+    private final Object nameArg1;
+    private final Object nameArg2;
+
+    private String name;
+    private int index;
+    private String doc;
+
+    protected AbstractKey(String nameFormat, Object nameArg1, Object nameArg2) {
+        this.nameFormat = nameFormat;
+        this.nameArg1 = nameArg1;
+        this.nameArg2 = nameArg2;
+        this.index = -1;
+    }
+
+    protected void setDoc(String doc) {
+        this.doc = doc;
+    }
+
+    @Override
+    public String getDoc() {
+        return doc;
+    }
+
+    @Override
+    public String getDocName() {
+        return getName();
+    }
+
+    public long getCurrentValue(DebugContext debug) {
+        ensureInitialized();
+        return debug.getMetricValue(index);
+    }
+
+    void setCurrentValue(DebugContext debug, long l) {
+        ensureInitialized();
+        debug.setMetricValue(index, l);
+    }
+
+    void ensureInitialized() {
+        if (index == -1) {
+            index = KeyRegistry.register(this);
+        }
+    }
+
+    void addToCurrentValue(DebugContext debug, long value) {
+        ensureInitialized();
+        debug.setMetricValue(index, debug.getMetricValue(index) + value);
+    }
+
+    /**
+     * Gets the globally unique index for the value represented by this object.
+     */
+    public int getIndex() {
+        ensureInitialized();
+        return index;
+    }
+
+    /**
+     * Gets the globally unique name for the value represented by this object.
+     */
+    @Override
+    public String getName() {
+        if (name == null) {
+            name = createName(nameFormat, nameArg1, nameArg2);
+        }
+        return name;
+    }
+
+    protected String createName(String format, Object arg1, Object arg2) {
+        return DebugContext.formatDebugName(format, arg1, arg2);
+    }
+
+    @Override
+    public String toString() {
+        return getName() + "@" + index;
+    }
+}
--- /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/AccumulatedKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+abstract class AccumulatedKey extends AbstractKey {
+    protected final AbstractKey flat;
+
+    static final String ACCUMULATED_KEY_SUFFIX = "_Accm";
+    static final String FLAT_KEY_SUFFIX = "_Flat";
+
+    protected AccumulatedKey(AbstractKey flat, String nameFormat, Object nameArg1, Object nameArg2) {
+        super(nameFormat, nameArg1, nameArg2);
+        this.flat = flat;
+    }
+
+    @Override
+    protected String createName(String format, Object arg1, Object arg2) {
+        return super.createName(format, arg1, arg2) + ACCUMULATED_KEY_SUFFIX;
+    }
+
+    @Override
+    public String getDocName() {
+        String name = getName();
+        return name.substring(0, name.length() - ACCUMULATED_KEY_SUFFIX.length());
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CSVUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/CSVUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -84,7 +84,11 @@
             return out;
         }
 
-        private static Object[] escapeArgs(char separator, char quote, char escape, Object... args) {
+        public static Object[] escapeArgs(Object... args) {
+            return escapeArgs(SEPARATOR, QUOTE, ESCAPE, args);
+        }
+
+        public static Object[] escapeArgs(char separator, char quote, char escape, Object... args) {
             String separatorStr = String.valueOf(separator);
             for (int i = 0; i < args.length; i++) {
                 Object obj = args[i];
--- /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/CloseableCounter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+/**
+ * A helper class for {@link AbstractKey}s that can nest and need to split out accumulated and flat
+ * values for some kind of counter-like measurement.
+ */
+public abstract class CloseableCounter implements DebugCloseable {
+
+    protected final DebugContext debug;
+    protected final CloseableCounter parent;
+    protected final AccumulatedKey counter;
+    protected final long start;
+    protected long nestedAmountToSubtract;
+
+    CloseableCounter(DebugContext debug, CloseableCounter parent, AccumulatedKey counter) {
+        this.debug = debug;
+        this.parent = parent;
+        this.start = getCounterValue();
+        this.counter = counter;
+    }
+
+    @Override
+    public DebugContext getDebug() {
+        return debug;
+    }
+
+    /**
+     * A hook for subclasses. Lets them perform custom operations with the value since the last
+     * invocation of {@link CloseableCounter#close()} of this accumulated
+     * {@link CloseableCounter#counter}.
+     *
+     * @param difference since the last invocation of this counter flat
+     */
+    protected void interceptDifferenceAccm(long difference) {
+        // hook for subclasses
+    }
+
+    /**
+     * A hook for subclasses. Lets them perform custom operations with the value since the last
+     * invocation of {@link CloseableCounter#close()} of this flat {@link CloseableCounter#counter}.
+     *
+     * @param difference since the last invocation of this counter flat
+     */
+    protected void interceptDifferenceFlat(long difference) {
+        // hook for subclasses
+    }
+
+    @Override
+    public void close() {
+        long end = getCounterValue();
+        long difference = end - start;
+        if (parent != null) {
+            if (!counter.getName().equals(parent.counter.getName())) {
+                parent.nestedAmountToSubtract += difference;
+                // Look for our counter in an outer scope and fix up
+                // the adjustment to the flat count
+                CloseableCounter ancestor = parent.parent;
+                while (ancestor != null) {
+                    if (ancestor.counter.getName().equals(counter.getName())) {
+                        ancestor.nestedAmountToSubtract -= difference;
+                        break;
+                    }
+                    ancestor = ancestor.parent;
+                }
+            }
+        }
+        long flatAmount = difference - nestedAmountToSubtract;
+        counter.addToCurrentValue(debug, difference);
+        counter.flat.addToCurrentValue(debug, flatAmount);
+        interceptDifferenceAccm(difference);
+        interceptDifferenceFlat(flatAmount);
+    }
+
+    abstract long getCounterValue();
+}
--- /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/CounterKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+/**
+ * A counter for some value of interest.
+ */
+public interface CounterKey extends MetricKey {
+
+    /**
+     * Adds 1 to this counter.
+     */
+    void increment(DebugContext debug);
+
+    /**
+     * Adds {@code value} to this counter.
+     */
+    void add(DebugContext debug, long value);
+
+    /**
+     * Gets the current value of this counter.
+     */
+    long getCurrentValue(DebugContext debug);
+
+    /**
+     * Determines if this counter is enabled.
+     */
+    boolean isEnabled(DebugContext debug);
+
+    @Override
+    CounterKey doc(String string);
+}
--- /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/CounterKeyImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import org.graalvm.util.Pair;
+
+class CounterKeyImpl extends AbstractKey implements CounterKey {
+
+    CounterKeyImpl(String format, Object arg1, Object arg2) {
+        super(format, arg1, arg2);
+    }
+
+    @Override
+    public void increment(DebugContext debug) {
+        add(debug, 1);
+    }
+
+    @Override
+    public Pair<String, String> toCSVFormat(long value) {
+        return Pair.create(String.valueOf(value), "");
+    }
+
+    @Override
+    public String toHumanReadableFormat(long value) {
+        return Long.toString(value);
+    }
+
+    @Override
+    public void add(DebugContext debug, long value) {
+        if (debug.isCounterEnabled(this)) {
+            addToCurrentValue(debug, value);
+        }
+    }
+
+    @Override
+    public boolean isEnabled(DebugContext debug) {
+        return debug.isCounterEnabled(this);
+    }
+
+    @Override
+    public CounterKey doc(String doc) {
+        setDoc(doc);
+        return this;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1786 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import static java.util.FormattableFlags.LEFT_JUSTIFY;
-import static java.util.FormattableFlags.UPPERCASE;
-import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT;
-import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.LOG_METHOD;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
-import org.graalvm.compiler.debug.DelegatingDebugConfig.Level;
-import org.graalvm.compiler.debug.internal.CounterImpl;
-import org.graalvm.compiler.debug.internal.DebugHistogramImpl;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.debug.internal.MemUseTrackerImpl;
-import org.graalvm.compiler.debug.internal.TimerImpl;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.options.OptionValuesAccess;
-import org.graalvm.compiler.serviceprovider.GraalServices;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-/**
- * Scope based debugging facility.
- *
- * This facility is {@linkplain #isEnabled() enabled} if any of the following hold when the
- * {@link Debug} class is initialized:
- * <ul>
- * <li>assertions are enabled for the {@link Debug} class</li>
- * <li>{@link Debug#params}{@code .enable} is {@code true}</li>
- * </ul>
- */
-public class Debug {
-
-    /**
-     * The option values available in this package.
-     */
-    static final OptionValues DEBUG_OPTIONS = GraalServices.loadSingle(OptionValuesAccess.class, true).getOptions();
-
-    static final Params params = new Params();
-
-    static {
-        // Load the service providers that may want to modify any of the
-        // parameters encapsulated by the Initialization class below.
-        for (DebugInitializationParticipant p : GraalServices.load(DebugInitializationParticipant.class)) {
-            p.apply(params);
-        }
-    }
-
-    /**
-     * The parameters for configuring the initialization of {@link Debug} class.
-     */
-    public static final class Params {
-        public boolean enable;
-        public boolean enableMethodFilter;
-        public boolean enableUnscopedTimers;
-        public boolean enableUnscopedCounters;
-        public boolean enableUnscopedMethodMetrics;
-        public boolean enableUnscopedMemUseTrackers;
-        public boolean interceptCount;
-        public boolean interceptTime;
-        public boolean interceptMem;
-
-        @SuppressWarnings("static-method")
-        public OptionValues getOptions() {
-            return DEBUG_OPTIONS;
-        }
-    }
-
-    @SuppressWarnings("all")
-    private static boolean initialize() {
-        return Assertions.ENABLED || params.enable || GraalDebugConfig.Options.ForceDebugEnable.getValue(DEBUG_OPTIONS);
-    }
-
-    private static final boolean ENABLED = initialize();
-
-    public static boolean isEnabled() {
-        return ENABLED;
-    }
-
-    public static boolean isDumpEnabledForMethod() {
-        if (!ENABLED) {
-            return false;
-        }
-        DebugConfig config = DebugScope.getConfig();
-        if (config == null) {
-            return false;
-        }
-        return config.isDumpEnabledForMethod();
-    }
-
-    /**
-     * A special dump level that indicates the dumping machinery is enabled but no dumps will be
-     * produced except through other options.
-     */
-    public static final int ENABLED_LEVEL = 0;
-
-    /**
-     * Basic debug level.
-     *
-     * For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
-     * after mid tier, after low tier.
-     *
-     * LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation
-     * stage, and after code installation.
-     */
-    public static final int BASIC_LEVEL = 1;
-
-    /**
-     * Informational debug level.
-     *
-     * HIR dumping: One graph after each applied top-level phase.
-     *
-     * LIR dumping: After each applied phase.
-     */
-    public static final int INFO_LEVEL = 2;
-
-    /**
-     * Verbose debug level.
-     *
-     * HIR dumping: One graph after each phase (including sub phases).
-     *
-     * LIR dumping: 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.
-     *
-     * LIR dumping: Dump CFG within phases where interesting.
-     */
-    public static final int DETAILED_LEVEL = 4;
-
-    /**
-     * Very detailed debug level.
-     *
-     * HIR dumping: Graphs per node granularity graph change (before/after change).
-     *
-     * LIR dumping: Intermediate CFGs of phases where interesting.
-     */
-    public static final int VERY_DETAILED_LEVEL = 5;
-
-    public static boolean isDumpEnabled(int dumpLevel) {
-        return ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel);
-    }
-
-    /**
-     * Determines if verification is enabled in the current method, regardless of the
-     * {@linkplain Debug#currentScope() current debug scope}.
-     *
-     * @see Debug#verify(Object, String)
-     */
-    public static boolean isVerifyEnabledForMethod() {
-        if (!ENABLED) {
-            return false;
-        }
-        DebugConfig config = DebugScope.getConfig();
-        if (config == null) {
-            return false;
-        }
-        return config.isVerifyEnabledForMethod();
-    }
-
-    /**
-     * Determines if verification is enabled in the {@linkplain Debug#currentScope() current debug
-     * scope}.
-     *
-     * @see Debug#verify(Object, String)
-     */
-    public static boolean isVerifyEnabled() {
-        return ENABLED && DebugScope.getInstance().isVerifyEnabled();
-    }
-
-    public static boolean isCountEnabled() {
-        return ENABLED && DebugScope.getInstance().isCountEnabled();
-    }
-
-    public static boolean isTimeEnabled() {
-        return ENABLED && DebugScope.getInstance().isTimeEnabled();
-    }
-
-    public static boolean isMemUseTrackingEnabled() {
-        return ENABLED && DebugScope.getInstance().isMemUseTrackingEnabled();
-    }
-
-    public static boolean isLogEnabledForMethod() {
-        if (!ENABLED) {
-            return false;
-        }
-        DebugConfig config = DebugScope.getConfig();
-        if (config == null) {
-            return false;
-        }
-        return config.isLogEnabledForMethod();
-    }
-
-    public static boolean isLogEnabled() {
-        return isLogEnabled(BASIC_LEVEL);
-    }
-
-    public static boolean isLogEnabled(int logLevel) {
-        return ENABLED && DebugScope.getInstance().isLogEnabled(logLevel);
-    }
-
-    public static boolean isMethodMeterEnabled() {
-        return ENABLED && DebugScope.getInstance().isMethodMeterEnabled();
-    }
-
-    @SuppressWarnings("unused")
-    public static Runnable decorateDebugRoot(Runnable runnable, String name, DebugConfig config) {
-        return runnable;
-    }
-
-    @SuppressWarnings("unused")
-    public static <T> Callable<T> decorateDebugRoot(Callable<T> callable, String name, DebugConfig config) {
-        return callable;
-    }
-
-    @SuppressWarnings("unused")
-    public static Runnable decorateScope(Runnable runnable, String name, Object... context) {
-        return runnable;
-    }
-
-    @SuppressWarnings("unused")
-    public static <T> Callable<T> decorateScope(Callable<T> callable, String name, Object... context) {
-        return callable;
-    }
-
-    /**
-     * Gets a string composed of the names in the current nesting of debug
-     * {@linkplain #scope(Object) scopes} separated by {@code '.'}.
-     */
-    public static String currentScope() {
-        if (ENABLED) {
-            return DebugScope.getInstance().getQualifiedName();
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Represents a debug scope entered by {@link Debug#scope(Object)} or
-     * {@link Debug#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is achieved
-     * via {@link #close()}.
-     */
-    public interface Scope extends AutoCloseable {
-        @Override
-        void close();
-    }
-
-    /**
-     * Creates and enters a new debug scope which will be a child of the current debug scope.
-     * <p>
-     * It is recommended to use the try-with-resource statement for managing entering and leaving
-     * debug scopes. For example:
-     *
-     * <pre>
-     * try (Scope s = Debug.scope(&quot;InliningGraph&quot;, inlineeGraph)) {
-     *     ...
-     * } catch (Throwable e) {
-     *     throw Debug.handle(e);
-     * }
-     * </pre>
-     *
-     * The {@code name} argument is subject to the following type based conversion before having
-     * {@link Object#toString()} called on it:
-     *
-     * <pre>
-     *     Type          | Conversion
-     * ------------------+-----------------
-     *  java.lang.Class  | arg.getSimpleName()
-     *                   |
-     * </pre>
-     *
-     * @param name the name of the new scope
-     * @param contextObjects an array of object to be appended to the {@linkplain #context()
-     *            current} debug context
-     * @throws Throwable used to enforce a catch block.
-     * @return the scope entered by this method which will be exited when its {@link Scope#close()}
-     *         method is called
-     */
-    public static Scope scope(Object name, Object[] contextObjects) throws Throwable {
-        if (ENABLED) {
-            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, contextObjects);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
-     * block can be omitted.
-     *
-     * @see #scope(Object, Object[])
-     */
-    public static Scope scope(Object name) {
-        if (ENABLED) {
-            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null);
-        } else {
-            return null;
-        }
-    }
-
-    public static Scope methodMetricsScope(Object name, DebugScope.ExtraInfo metaInfo, boolean newId, Object... context) {
-        if (ENABLED) {
-            return DebugScope.getInstance().enhanceWithExtraInfo(convertFormatArg(name).toString(), metaInfo, newId, context);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @see #scope(Object, Object[])
-     * @param context an object to be appended to the {@linkplain #context() current} debug context
-     */
-    public static Scope scope(Object name, Object context) throws Throwable {
-        if (ENABLED) {
-            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @see #scope(Object, Object[])
-     * @param context1 first object to be appended to the {@linkplain #context() current} debug
-     *            context
-     * @param context2 second object to be appended to the {@linkplain #context() current} debug
-     *            context
-     */
-    public static Scope scope(Object name, Object context1, Object context2) throws Throwable {
-        if (ENABLED) {
-            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @see #scope(Object, Object[])
-     * @param context1 first object to be appended to the {@linkplain #context() current} debug
-     *            context
-     * @param context2 second object to be appended to the {@linkplain #context() current} debug
-     *            context
-     * @param context3 third object to be appended to the {@linkplain #context() current} debug
-     *            context
-     */
-    public static Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
-        if (ENABLED) {
-            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2, context3);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Creates and enters a new debug scope which will be disjoint from the current debug scope.
-     * <p>
-     * It is recommended to use the try-with-resource statement for managing entering and leaving
-     * debug scopes. For example:
-     *
-     * <pre>
-     * try (Scope s = Debug.sandbox(&quot;CompilingStub&quot;, null, stubGraph)) {
-     *     ...
-     * } catch (Throwable e) {
-     *     throw Debug.handle(e);
-     * }
-     * </pre>
-     *
-     * @param name the name of the new scope
-     * @param config the debug configuration to use for the new scope
-     * @param context objects to be appended to the {@linkplain #context() current} debug context
-     * @return the scope entered by this method which will be exited when its {@link Scope#close()}
-     *         method is called
-     */
-    public static Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
-        if (ENABLED) {
-            DebugConfig sandboxConfig = config == null ? silentConfig() : config;
-            return DebugScope.getInstance().scope(name, sandboxConfig, context);
-        } else {
-            return null;
-        }
-    }
-
-    public static Scope forceLog() throws Throwable {
-        ArrayList<Object> context = new ArrayList<>();
-        for (Object obj : context()) {
-            context.add(obj);
-        }
-        return Debug.sandbox("forceLog", new DelegatingDebugConfig().override(Level.LOG, Integer.MAX_VALUE).enable(LOG_METHOD), context.toArray());
-    }
-
-    /**
-     * Opens a scope in which exception {@linkplain DebugConfig#interceptException(Throwable)
-     * interception} is disabled. It is recommended to use the try-with-resource statement for
-     * managing entering and leaving such scopes:
-     *
-     * <pre>
-     * try (DebugConfigScope s = Debug.disableIntercept()) {
-     *     ...
-     * }
-     * </pre>
-     *
-     * This is particularly useful to suppress extraneous output in JUnit tests that are expected to
-     * throw an exception.
-     */
-    public static DebugConfigScope disableIntercept() {
-        return Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT));
-    }
-
-    /**
-     * Handles an exception in the context of the debug scope just exited. The just exited scope
-     * must have the current scope as its parent which will be the case if the try-with-resource
-     * pattern recommended by {@link #scope(Object)} and
-     * {@link #sandbox(CharSequence, DebugConfig, Object...)} is used
-     *
-     * @see #scope(Object, Object[])
-     * @see #sandbox(CharSequence, DebugConfig, Object...)
-     */
-    public static RuntimeException handle(Throwable exception) {
-        if (ENABLED) {
-            return DebugScope.getInstance().handle(exception);
-        } else {
-            if (exception instanceof Error) {
-                throw (Error) exception;
-            }
-            if (exception instanceof RuntimeException) {
-                throw (RuntimeException) exception;
-            }
-            throw new RuntimeException(exception);
-        }
-    }
-
-    public static void log(String msg) {
-        log(BASIC_LEVEL, msg);
-    }
-
-    /**
-     * Prints a message to the current debug scope's logging stream if logging is enabled.
-     *
-     * @param msg the message to log
-     */
-    public static void log(int logLevel, String msg) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, msg);
-        }
-    }
-
-    public static void log(String format, Object arg) {
-        log(BASIC_LEVEL, format, arg);
-    }
-
-    /**
-     * Prints a message to the current debug scope's logging stream if logging is enabled.
-     *
-     * @param format a format string
-     * @param arg the argument referenced by the format specifiers in {@code format}
-     */
-    public static void log(int logLevel, String format, Object arg) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg);
-        }
-    }
-
-    public static void log(String format, int arg) {
-        log(BASIC_LEVEL, format, arg);
-    }
-
-    /**
-     * Prints a message to the current debug scope's logging stream if logging is enabled.
-     *
-     * @param format a format string
-     * @param arg the argument referenced by the format specifiers in {@code format}
-     */
-    public static void log(int logLevel, String format, int arg) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2) {
-        log(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2);
-        }
-    }
-
-    public static void log(String format, int arg1, Object arg2) {
-        log(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, int arg1, Object arg2) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2);
-        }
-    }
-
-    public static void log(String format, Object arg1, int arg2) {
-        log(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, int arg2) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2);
-        }
-    }
-
-    public static void log(String format, int arg1, int arg2) {
-        log(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, int arg1, int arg2) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2, Object arg3) {
-        log(BASIC_LEVEL, format, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3);
-        }
-    }
-
-    public static void log(String format, int arg1, int arg2, int arg3) {
-        log(BASIC_LEVEL, format, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, int arg1, int arg2, int arg3) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
-        }
-    }
-
-    public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object 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_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
-    }
-
-    /**
-     * @see #log(int, String, Object)
-     */
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
-        }
-    }
-
-    public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
-        }
-    }
-
-    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_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) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
-        }
-    }
-
-    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_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) {
-        if (ENABLED) {
-            DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
-        }
-    }
-
-    public static void logv(String format, Object... args) {
-        logv(BASIC_LEVEL, format, args);
-    }
-
-    /**
-     * Prints a message to the current debug scope's logging stream. This method must only be called
-     * if debugging is {@linkplain Debug#isEnabled() enabled} as it incurs allocation at the call
-     * site. If possible, call one of the other {@code log()} methods in this class that take a
-     * fixed number of parameters.
-     *
-     * @param format a format string
-     * @param args the arguments referenced by the format specifiers in {@code format}
-     */
-    public static void logv(int logLevel, String format, Object... args) {
-        if (!ENABLED) {
-            throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()");
-        }
-        DebugScope.getInstance().log(logLevel, format, args);
-    }
-
-    /**
-     * This override exists to catch cases when {@link #log(String, Object)} is called with one
-     * argument bound to a varargs method parameter. It will bind to this method instead of the
-     * single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void log(String format, Object[] args) {
-        assert false : "shouldn't use this";
-        log(BASIC_LEVEL, format, args);
-    }
-
-    /**
-     * This override exists to catch cases when {@link #log(int, String, Object)} is called with one
-     * argument bound to a varargs method parameter. It will bind to this method instead of the
-     * single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void log(int logLevel, String format, Object[] args) {
-        assert false : "shouldn't use this";
-        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);
-        }
-    }
-
-    public static void dump(int dumpLevel, Object object, String format, Object arg) {
-        if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
-            DebugScope.getInstance().dump(dumpLevel, object, format, arg);
-        }
-    }
-
-    public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) {
-        if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
-            DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2);
-        }
-    }
-
-    public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) {
-        if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
-            DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2, arg3);
-        }
-    }
-
-    /**
-     * This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called
-     * with one argument bound to a varargs method parameter. It will bind to this method instead of
-     * the single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void dump(int dumpLevel, Object object, String format, Object[] args) {
-        assert false : "shouldn't use this";
-        if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
-            DebugScope.getInstance().dump(dumpLevel, object, format, args);
-        }
-    }
-
-    /**
-     * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig()
-     * config} to perform verification on a given object.
-     *
-     * @param object object to verify
-     * @param message description of verification context
-     *
-     * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...)
-     */
-    public static void verify(Object object, String message) {
-        if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
-            DebugScope.getInstance().verify(object, message);
-        }
-    }
-
-    /**
-     * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig()
-     * config} to perform verification on a given object.
-     *
-     * @param object object to verify
-     * @param format a format string for the description of the verification context
-     * @param arg the argument referenced by the format specifiers in {@code format}
-     *
-     * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...)
-     */
-    public static void verify(Object object, String format, Object arg) {
-        if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
-            DebugScope.getInstance().verify(object, format, arg);
-        }
-    }
-
-    /**
-     * This override exists to catch cases when {@link #verify(Object, String, Object)} is called
-     * with one argument bound to a varargs method parameter. It will bind to this method instead of
-     * the single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void verify(Object object, String format, Object[] args) {
-        assert false : "shouldn't use this";
-        if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
-            DebugScope.getInstance().verify(object, format, args);
-        }
-    }
-
-    /**
-     * Opens a new indentation level (by adding some spaces) based on the current indentation level.
-     * This should be used in a {@linkplain Indent try-with-resources} pattern.
-     *
-     * @return an object that reverts to the current indentation level when
-     *         {@linkplain Indent#close() closed} or null if debugging is disabled
-     * @see #logAndIndent(int, String)
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent indent() {
-        if (ENABLED) {
-            DebugScope scope = DebugScope.getInstance();
-            return scope.pushIndentLogger();
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String msg) {
-        return logAndIndent(BASIC_LEVEL, msg);
-    }
-
-    /**
-     * A convenience function which combines {@link #log(String)} and {@link #indent()}.
-     *
-     * @param msg the message to log
-     * @return an object that reverts to the current indentation level when
-     *         {@linkplain Indent#close() closed} or null if debugging is disabled
-     */
-    public static Indent logAndIndent(int logLevel, String msg) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, msg);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg) {
-        return logAndIndent(BASIC_LEVEL, format, arg);
-    }
-
-    /**
-     * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
-     *
-     * @param format a format string
-     * @param arg the argument referenced by the format specifiers in {@code format}
-     * @return an object that reverts to the current indentation level when
-     *         {@linkplain Indent#close() closed} or null if debugging is disabled
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, int arg) {
-        return logAndIndent(BASIC_LEVEL, format, arg);
-    }
-
-    /**
-     * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
-     *
-     * @param format a format string
-     * @param arg the argument referenced by the format specifiers in {@code format}
-     * @return an object that reverts to the current indentation level when
-     *         {@linkplain Indent#close() closed} or null if debugging is disabled
-     */
-    public static Indent logAndIndent(int logLevel, String format, int arg) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, int arg1, Object arg2) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, int arg2) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, int arg1, int arg2) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, Object arg2) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5);
-        }
-        return null;
-    }
-
-    public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
-    }
-
-    /**
-     * @see #logAndIndent(int, String, Object)
-     */
-    public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-        if (ENABLED && Debug.isLogEnabled(logLevel)) {
-            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
-        }
-        return null;
-    }
-
-    /**
-     * A convenience function which combines {@link #logv(int, String, Object...)} and
-     * {@link #indent()}.
-     *
-     * @param format a format string
-     * @param args the arguments referenced by the format specifiers in {@code format}
-     * @return an object that reverts to the current indentation level when
-     *         {@linkplain Indent#close() closed} or null if debugging is disabled
-     */
-    public static Indent logvAndIndent(int logLevel, String format, Object... args) {
-        if (ENABLED) {
-            if (Debug.isLogEnabled(logLevel)) {
-                return logvAndIndentInternal(logLevel, format, args);
-            }
-            return null;
-        }
-        throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()");
-    }
-
-    private static Indent logvAndIndentInternal(int logLevel, String format, Object... args) {
-        assert ENABLED && Debug.isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()";
-        DebugScope scope = DebugScope.getInstance();
-        scope.log(logLevel, format, args);
-        return scope.pushIndentLogger();
-    }
-
-    /**
-     * This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with
-     * one argument bound to a varargs method parameter. It will bind to this method instead of the
-     * single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void logAndIndent(String format, Object[] args) {
-        assert false : "shouldn't use this";
-        logAndIndent(BASIC_LEVEL, format, args);
-    }
-
-    /**
-     * This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called
-     * with one argument bound to a varargs method parameter. It will bind to this method instead of
-     * the single arg variant and produce a deprecation warning instead of silently wrapping the
-     * Object[] inside of another Object[].
-     */
-    @Deprecated
-    public static void logAndIndent(int logLevel, String format, Object[] args) {
-        assert false : "shouldn't use this";
-        logvAndIndent(logLevel, format, args);
-    }
-
-    public static Iterable<Object> context() {
-        if (ENABLED) {
-            return DebugScope.getInstance().getCurrentContext();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    public static <T> List<T> contextSnapshot(Class<T> clazz) {
-        if (ENABLED) {
-            List<T> result = new ArrayList<>();
-            for (Object o : context()) {
-                if (clazz.isInstance(o)) {
-                    result.add((T) o);
-                }
-            }
-            return result;
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    /**
-     * Searches the current debug scope, bottom up, for a context object that is an instance of a
-     * given type. The first such object found is returned.
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> T contextLookup(Class<T> clazz) {
-        if (ENABLED) {
-            for (Object o : context()) {
-                if (clazz.isInstance(o)) {
-                    return ((T) o);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Creates a {@linkplain DebugMemUseTracker memory use tracker} that is enabled iff debugging is
-     * {@linkplain #isEnabled() enabled}.
-     * <p>
-     * A disabled tracker has virtually no overhead.
-     */
-    public static DebugMemUseTracker memUseTracker(CharSequence name) {
-        if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
-            return VOID_MEM_USE_TRACKER;
-        }
-        return createMemUseTracker("%s", name, null);
-    }
-
-    /**
-     * Creates a debug memory use tracker. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.memUseTracker(format, arg, null)
-     * </pre>
-     *
-     * except that the string formatting only happens if mem tracking is enabled.
-     *
-     * @see #counter(String, Object, Object)
-     */
-    public static DebugMemUseTracker memUseTracker(String format, Object arg) {
-        if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
-            return VOID_MEM_USE_TRACKER;
-        }
-        return createMemUseTracker(format, arg, null);
-    }
-
-    /**
-     * Creates a debug memory use tracker. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.memUseTracker(String.format(format, arg1, arg2))
-     * </pre>
-     *
-     * except that the string formatting only happens if memory use tracking is enabled. In
-     * addition, each argument is subject to the following type based conversion before being passed
-     * as an argument to {@link String#format(String, Object...)}:
-     *
-     * <pre>
-     *     Type          | Conversion
-     * ------------------+-----------------
-     *  java.lang.Class  | arg.getSimpleName()
-     *                   |
-     * </pre>
-     *
-     * @see #memUseTracker(CharSequence)
-     */
-    public static DebugMemUseTracker memUseTracker(String format, Object arg1, Object arg2) {
-        if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
-            return VOID_MEM_USE_TRACKER;
-        }
-        return createMemUseTracker(format, arg1, arg2);
-    }
-
-    private static DebugMemUseTracker createMemUseTracker(String format, Object arg1, Object arg2) {
-        String name = formatDebugName(format, arg1, arg2);
-        return DebugValueFactory.createMemUseTracker(name, !isUnconditionalMemUseTrackingEnabled);
-    }
-
-    /**
-     * Creates a {@linkplain DebugCounter counter} that is enabled iff debugging is
-     * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding
-     * {@value #ENABLE_COUNTER_PROPERTY_NAME_PREFIX} to {@code name} is
-     * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the
-     * returned counter is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is
-     * conditional.
-     * <p>
-     * A disabled counter has virtually no overhead.
-     */
-    public static DebugCounter counter(CharSequence name) {
-        if (!areUnconditionalCountersEnabled() && !ENABLED) {
-            return VOID_COUNTER;
-        }
-        return createCounter("%s", name, null);
-    }
-
-    /**
-     * Creates a {@link DebugMethodMetrics metric} that is enabled iff debugging is
-     * {@link #isEnabled() enabled}.
-     */
-    public static DebugMethodMetrics methodMetrics(ResolvedJavaMethod method) {
-        if (isMethodMeterEnabled() && method != null) {
-            return MethodMetricsImpl.getMethodMetrics(method);
-        }
-        return VOID_MM;
-    }
-
-    public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
-        if (flags == 0 && width < 0) {
-            return s;
-        }
-        StringBuilder sb = new StringBuilder(s);
-
-        // apply width and justification
-        int len = sb.length();
-        if (len < width) {
-            for (int i = 0; i < width - len; i++) {
-                if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
-                    sb.append(' ');
-                } else {
-                    sb.insert(0, ' ');
-                }
-            }
-        }
-
-        String res = sb.toString();
-        if ((flags & UPPERCASE) == UPPERCASE) {
-            res = res.toUpperCase();
-        }
-        return res;
-    }
-
-    /**
-     * Creates a debug counter. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.counter(format, arg, null)
-     * </pre>
-     *
-     * except that the string formatting only happens if count is enabled.
-     *
-     * @see #counter(String, Object, Object)
-     */
-    public static DebugCounter counter(String format, Object arg) {
-        if (!areUnconditionalCountersEnabled() && !ENABLED) {
-            return VOID_COUNTER;
-        }
-        return createCounter(format, arg, null);
-    }
-
-    /**
-     * Creates a debug counter. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.counter(String.format(format, arg1, arg2))
-     * </pre>
-     *
-     * except that the string formatting only happens if count is enabled. In addition, each
-     * argument is subject to the following type based conversion before being passed as an argument
-     * to {@link String#format(String, Object...)}:
-     *
-     * <pre>
-     *     Type          | Conversion
-     * ------------------+-----------------
-     *  java.lang.Class  | arg.getSimpleName()
-     *                   |
-     * </pre>
-     *
-     * @see #counter(CharSequence)
-     */
-    public static DebugCounter counter(String format, Object arg1, Object arg2) {
-        if (!areUnconditionalCountersEnabled() && !ENABLED) {
-            return VOID_COUNTER;
-        }
-        return createCounter(format, arg1, arg2);
-    }
-
-    private static DebugCounter createCounter(String format, Object arg1, Object arg2) {
-        String name = formatDebugName(format, arg1, arg2);
-        boolean conditional = enabledCounters == null || !findMatch(enabledCounters, enabledCountersSubstrings, name);
-        if (!ENABLED && conditional) {
-            return VOID_COUNTER;
-        }
-        return DebugValueFactory.createCounter(name, conditional);
-    }
-
-    /**
-     * Changes the debug configuration for the current thread.
-     *
-     * @param config new configuration to use for the current thread
-     * @return an object that when {@linkplain DebugConfigScope#close() closed} will restore the
-     *         debug configuration for the current thread to what it was before this method was
-     *         called
-     */
-    public static DebugConfigScope setConfig(DebugConfig config) {
-        if (ENABLED) {
-            return new DebugConfigScope(config);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Creates an object for counting value frequencies.
-     */
-    public static DebugHistogram createHistogram(String name) {
-        return new DebugHistogramImpl(name);
-    }
-
-    public static DebugConfig silentConfig() {
-        return fixedConfig(new OptionValues(OptionValues.newOptionMap()), 0, 0, false, false, false, false, false, Collections.<DebugDumpHandler> emptyList(),
-                        Collections.<DebugVerifyHandler> emptyList(), null);
-    }
-
-    public static DebugConfig fixedConfig(OptionValues options, final int logLevel, final int dumpLevel, final boolean isCountEnabled, final boolean isMemUseTrackingEnabled,
-                    final boolean isTimerEnabled,
-                    final boolean isVerifyEnabled, final boolean isMMEnabled, final Collection<DebugDumpHandler> dumpHandlers, final Collection<DebugVerifyHandler> verifyHandlers,
-                    final PrintStream output) {
-        return new DebugConfig() {
-
-            @Override
-            public OptionValues getOptions() {
-                return options;
-            }
-
-            @Override
-            public int getLogLevel() {
-                return logLevel;
-            }
-
-            @Override
-            public boolean isLogEnabledForMethod() {
-                return logLevel > 0;
-            }
-
-            @Override
-            public boolean isCountEnabled() {
-                return isCountEnabled;
-            }
-
-            @Override
-            public boolean isMemUseTrackingEnabled() {
-                return isMemUseTrackingEnabled;
-            }
-
-            @Override
-            public int getDumpLevel() {
-                return dumpLevel;
-            }
-
-            @Override
-            public boolean isDumpEnabledForMethod() {
-                return dumpLevel > 0;
-            }
-
-            @Override
-            public boolean isVerifyEnabled() {
-                return isVerifyEnabled;
-            }
-
-            @Override
-            public boolean isVerifyEnabledForMethod() {
-                return isVerifyEnabled;
-            }
-
-            @Override
-            public boolean isMethodMeterEnabled() {
-                return isMMEnabled;
-            }
-
-            @Override
-            public boolean isTimeEnabled() {
-                return isTimerEnabled;
-            }
-
-            @Override
-            public RuntimeException interceptException(Throwable e) {
-                return null;
-            }
-
-            @Override
-            public Collection<DebugDumpHandler> dumpHandlers() {
-                return dumpHandlers;
-            }
-
-            @Override
-            public Collection<DebugVerifyHandler> verifyHandlers() {
-                return verifyHandlers;
-            }
-
-            @Override
-            public PrintStream output() {
-                return output;
-            }
-
-            @Override
-            public void addToContext(Object o) {
-            }
-
-            @Override
-            public void removeFromContext(Object o) {
-            }
-        };
-    }
-
-    private static final DebugCounter VOID_COUNTER = new DebugCounter() {
-
-        @Override
-        public void increment() {
-        }
-
-        @Override
-        public void add(long value) {
-        }
-
-        @Override
-        public void setConditional(boolean flag) {
-            throw new InternalError("Cannot make void counter conditional");
-        }
-
-        @Override
-        public boolean isConditional() {
-            return false;
-        }
-
-        @Override
-        public long getCurrentValue() {
-            return 0L;
-        }
-    };
-
-    private static final DebugMethodMetrics VOID_MM = new DebugMethodMetrics() {
-
-        @Override
-        public void addToMetric(long value, String metricName) {
-        }
-
-        @Override
-        public void addToMetric(long value, String format, Object arg1) {
-        }
-
-        @Override
-        public void addToMetric(long value, String format, Object arg1, Object arg2) {
-        }
-
-        @Override
-        public void addToMetric(long value, String format, Object arg1, Object arg2, Object arg3) {
-        }
-
-        @Override
-        public void incrementMetric(String metricName) {
-        }
-
-        @Override
-        public void incrementMetric(String format, Object arg1) {
-        }
-
-        @Override
-        public void incrementMetric(String format, Object arg1, Object arg2) {
-        }
-
-        @Override
-        public void incrementMetric(String format, Object arg1, Object arg2, Object arg3) {
-        }
-
-        @Override
-        public long getCurrentMetricValue(String metricName) {
-            return 0;
-        }
-
-        @Override
-        public long getCurrentMetricValue(String format, Object arg1) {
-            return 0;
-        }
-
-        @Override
-        public long getCurrentMetricValue(String format, Object arg1, Object arg2) {
-            return 0;
-        }
-
-        @Override
-        public long getCurrentMetricValue(String format, Object arg1, Object arg2, Object arg3) {
-            return 0;
-        }
-
-        @Override
-        public ResolvedJavaMethod getMethod() {
-            return null;
-        }
-
-    };
-
-    private static final DebugMemUseTracker VOID_MEM_USE_TRACKER = new DebugMemUseTracker() {
-
-        @Override
-        public DebugCloseable start() {
-            return DebugCloseable.VOID_CLOSEABLE;
-        }
-
-        @Override
-        public long getCurrentValue() {
-            return 0;
-        }
-    };
-
-    /**
-     * @see #timer(CharSequence)
-     */
-    public static final String ENABLE_TIMER_PROPERTY_NAME_PREFIX = "graaldebug.timer.";
-
-    /**
-     * @see #counter(CharSequence)
-     */
-    public static final String ENABLE_COUNTER_PROPERTY_NAME_PREFIX = "graaldebug.counter.";
-
-    /**
-     * Set of unconditionally enabled counters. Possible values and their meanings:
-     * <ul>
-     * <li>{@code null}: no unconditionally enabled counters</li>
-     * <li>{@code isEmpty()}: all counters are unconditionally enabled</li>
-     * <li>{@code !isEmpty()}: use {@link #findMatch(Set, Set, String)} on this set and
-     * {@link #enabledCountersSubstrings} to determine which counters are unconditionally enabled
-     * </li>
-     * </ul>
-     */
-    private static final Set<String> enabledCounters;
-
-    /**
-     * Set of unconditionally enabled timers. Same interpretation of values as for
-     * {@link #enabledCounters}.
-     */
-    private static final Set<String> enabledTimers;
-
-    private static final Set<String> enabledCountersSubstrings = new HashSet<>();
-    private static final Set<String> enabledTimersSubstrings = new HashSet<>();
-
-    /**
-     * Specifies if all mem use trackers are unconditionally enabled.
-     */
-    private static final boolean isUnconditionalMemUseTrackingEnabled;
-
-    static {
-        Set<String> counters = new HashSet<>();
-        Set<String> timers = new HashSet<>();
-        parseCounterAndTimerSystemProperties(counters, timers, enabledCountersSubstrings, enabledTimersSubstrings);
-        counters = counters.isEmpty() && enabledCountersSubstrings.isEmpty() ? null : counters;
-        timers = timers.isEmpty() && enabledTimersSubstrings.isEmpty() ? null : timers;
-        if (counters == null && params.enableUnscopedCounters && !params.enableMethodFilter) {
-            counters = Collections.emptySet();
-        }
-        if (timers == null && params.enableUnscopedTimers && !params.enableMethodFilter) {
-            timers = Collections.emptySet();
-        }
-        enabledCounters = counters;
-        enabledTimers = timers;
-        isUnconditionalMemUseTrackingEnabled = params.enableUnscopedMemUseTrackers;
-        DebugValueFactory = initDebugValueFactory();
-    }
-
-    private static DebugValueFactory initDebugValueFactory() {
-        return new DebugValueFactory() {
-
-            @Override
-            public DebugTimer createTimer(String name, boolean conditional) {
-                return new TimerImpl(name, conditional, params.interceptTime);
-            }
-
-            @Override
-            public DebugCounter createCounter(String name, boolean conditional) {
-                return CounterImpl.create(name, conditional, params.interceptCount);
-            }
-
-            @Override
-            public DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method) {
-                return MethodMetricsImpl.getMethodMetrics(method);
-            }
-
-            @Override
-            public DebugMemUseTracker createMemUseTracker(String name, boolean conditional) {
-                return new MemUseTrackerImpl(name, conditional, params.interceptMem);
-            }
-        };
-    }
-
-    private static DebugValueFactory DebugValueFactory;
-
-    public static void setDebugValueFactory(DebugValueFactory factory) {
-        Objects.requireNonNull(factory);
-        DebugValueFactory = factory;
-    }
-
-    public static DebugValueFactory getDebugValueFactory() {
-        return DebugValueFactory;
-    }
-
-    private static boolean findMatch(Set<String> haystack, Set<String> haystackSubstrings, String needle) {
-        if (haystack.isEmpty() && haystackSubstrings.isEmpty()) {
-            // Empty haystack means match all
-            return true;
-        }
-        if (haystack.contains(needle)) {
-            return true;
-        }
-        if (!haystackSubstrings.isEmpty()) {
-            for (String h : haystackSubstrings) {
-                if (needle.startsWith(h)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public static boolean areUnconditionalTimersEnabled() {
-        return enabledTimers != null;
-    }
-
-    public static boolean areUnconditionalCountersEnabled() {
-        return enabledCounters != null;
-    }
-
-    public static boolean isMethodFilteringEnabled() {
-        return params.enableMethodFilter;
-    }
-
-    public static boolean areUnconditionalMethodMetricsEnabled() {
-        // we do not collect mm substrings
-        return params.enableUnscopedMethodMetrics;
-    }
-
-    protected static void parseCounterAndTimerSystemProperties(Set<String> counters, Set<String> timers, Set<String> countersSubstrings, Set<String> timersSubstrings) {
-        do {
-            try {
-                for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) {
-                    String name = e.getKey().toString();
-                    if (name.startsWith(ENABLE_COUNTER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) {
-                        if (name.endsWith("*")) {
-                            countersSubstrings.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length(), name.length() - 1));
-                        } else {
-                            counters.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length()));
-                        }
-                    }
-                    if (name.startsWith(ENABLE_TIMER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) {
-                        if (name.endsWith("*")) {
-                            timersSubstrings.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length(), name.length() - 1));
-                        } else {
-                            timers.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length()));
-                        }
-                    }
-                }
-                return;
-            } catch (ConcurrentModificationException e) {
-                // Iterating over the system properties may race with another thread that is
-                // updating the system properties. Simply try again in this case.
-            }
-        } while (true);
-    }
-
-    /**
-     * Creates a {@linkplain DebugTimer timer} that is enabled iff debugging is
-     * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding
-     * {@value #ENABLE_TIMER_PROPERTY_NAME_PREFIX} to {@code name} is
-     * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the
-     * returned timer is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is
-     * conditional.
-     * <p>
-     * A disabled timer has virtually no overhead.
-     */
-    public static DebugTimer timer(CharSequence name) {
-        if (!areUnconditionalTimersEnabled() && !ENABLED) {
-            return VOID_TIMER;
-        }
-        return createTimer("%s", name, null);
-    }
-
-    /**
-     * Creates a debug timer. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.timer(format, arg, null)
-     * </pre>
-     *
-     * except that the string formatting only happens if timing is enabled.
-     *
-     * @see #timer(String, Object, Object)
-     */
-    public static DebugTimer timer(String format, Object arg) {
-        if (!areUnconditionalTimersEnabled() && !ENABLED) {
-            return VOID_TIMER;
-        }
-        return createTimer(format, arg, null);
-    }
-
-    /**
-     * Creates a debug timer. Invoking this method is equivalent to:
-     *
-     * <pre>
-     * Debug.timer(String.format(format, arg1, arg2))
-     * </pre>
-     *
-     * except that the string formatting only happens if timing is enabled. In addition, each
-     * argument is subject to the following type based conversion before being passed as an argument
-     * to {@link String#format(String, Object...)}:
-     *
-     * <pre>
-     *     Type          | Conversion
-     * ------------------+-----------------
-     *  java.lang.Class  | arg.getSimpleName()
-     *                   |
-     * </pre>
-     *
-     * @see #timer(CharSequence)
-     */
-    public static DebugTimer timer(String format, Object arg1, Object arg2) {
-        if (!areUnconditionalTimersEnabled() && !ENABLED) {
-            return VOID_TIMER;
-        }
-        return createTimer(format, arg1, arg2);
-    }
-
-    /**
-     * There are paths where construction of formatted class names are common and the code below is
-     * surprisingly expensive, so compute it once and cache it.
-     */
-    private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
-        @Override
-        protected String computeValue(Class<?> c) {
-            final String simpleName = c.getSimpleName();
-            Class<?> enclosingClass = c.getEnclosingClass();
-            if (enclosingClass != null) {
-                String prefix = "";
-                while (enclosingClass != null) {
-                    prefix = enclosingClass.getSimpleName() + "_" + prefix;
-                    enclosingClass = enclosingClass.getEnclosingClass();
-                }
-                return prefix + simpleName;
-            } else {
-                return simpleName;
-            }
-        }
-    };
-
-    public static Object convertFormatArg(Object arg) {
-        if (arg instanceof Class) {
-            return formattedClassName.get((Class<?>) arg);
-        }
-        return arg;
-    }
-
-    private static String formatDebugName(String format, Object arg1, Object arg2) {
-        return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
-    }
-
-    private static DebugTimer createTimer(String format, Object arg1, Object arg2) {
-        String name = formatDebugName(format, arg1, arg2);
-        boolean conditional = enabledTimers == null || !findMatch(enabledTimers, enabledTimersSubstrings, name);
-        if (!ENABLED && conditional) {
-            return VOID_TIMER;
-        }
-        return DebugValueFactory.createTimer(name, conditional);
-    }
-
-    private static final DebugTimer VOID_TIMER = new DebugTimer() {
-
-        @Override
-        public DebugCloseable start() {
-            return DebugCloseable.VOID_CLOSEABLE;
-        }
-
-        @Override
-        public void setConditional(boolean flag) {
-            throw new InternalError("Cannot make void timer conditional");
-        }
-
-        @Override
-        public boolean isConditional() {
-            return false;
-        }
-
-        @Override
-        public long getCurrentValue() {
-            return 0L;
-        }
-
-        @Override
-        public TimeUnit getTimeUnit() {
-            return null;
-        }
-    };
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugCloseable.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugCloseable.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,6 +34,13 @@
         }
     };
 
+    /**
+     * Gets the debug context associated with this object.
+     */
+    default DebugContext getDebug() {
+        return null;
+    }
+
     @Override
     void close();
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfig.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfig.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,86 +25,68 @@
 import java.io.PrintStream;
 import java.util.Collection;
 
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.options.OptionValues;
 
+import jdk.vm.ci.meta.JavaMethod;
+
 public interface DebugConfig {
 
     /**
-     * Returns the option values that can be used to configure details of debug clients.
+     * Returns the option values used to configure this object.
      */
     OptionValues getOptions();
 
     /**
-     * Determines the current log level in the {@linkplain Debug#currentScope() current debug scope}
-     * .
+     * Determines the current log level in {@code scope}.
      */
-    int getLogLevel();
+    int getLogLevel(DebugContext.Scope scope);
 
     /**
-     * Determines the current dump level in the {@linkplain Debug#currentScope() current debug
-     * scope}.
+     * Determines the current dump level in {@code scope}.
      */
-    int getDumpLevel();
+    int getDumpLevel(DebugContext.Scope scope);
 
     /**
-     * Determines if logging can be enabled in the current method, regardless of the
-     * {@linkplain Debug#currentScope() current debug scope}.
+     * Determines if logging is enabled for any {@link JavaMethod} in {@code scope}'s
+     * {@linkplain Scope#getCurrentContext() context}.
      */
-    boolean isLogEnabledForMethod();
+    boolean isLogEnabledForMethod(DebugContext.Scope scope);
 
     /**
-     * Determines if counting is enabled in the {@linkplain Debug#currentScope() current debug
-     * scope}.
+     * Determines if counting is enabled in {@code scope}.
      *
-     * @see Debug#counter(CharSequence)
+     * @see DebugContext#counter(CharSequence)
      */
-    boolean isCountEnabled();
-
-    /**
-     * Determines if memory use tracking is enabled in the {@linkplain Debug#currentScope() current
-     * debug scope}.
-     *
-     * @see Debug#memUseTracker(CharSequence)
-     */
-    boolean isMemUseTrackingEnabled();
+    boolean isCountEnabled(DebugContext.Scope scope);
 
     /**
-     * Determines if dumping can be enabled in the current method, regardless of the
-     * {@linkplain Debug#currentScope() current debug scope}.
+     * Determines if memory use tracking is {@code scope}.
+     *
+     * @see DebugContext#memUseTracker(CharSequence)
      */
-    boolean isDumpEnabledForMethod();
+    boolean isMemUseTrackingEnabled(DebugContext.Scope scope);
 
     /**
-     * @see Debug#isVerifyEnabled()
+     * Determines if dumping is enabled for any {@link JavaMethod} in {@code scope}'s
+     * {@linkplain Scope#getCurrentContext() context}.
      */
-    boolean isVerifyEnabled();
-
-    /**
-     * @see Debug#isVerifyEnabledForMethod()
-     */
-    boolean isVerifyEnabledForMethod();
+    boolean isDumpEnabledForMethod(DebugContext.Scope scope);
 
     /**
-     * @see Debug#isMethodMeterEnabled()
+     * @see DebugContext#isVerifyEnabled()
      */
-    boolean isMethodMeterEnabled();
-
-    /**
-     * Adds an object the context used by this configuration to do filtering.
-     */
-    void addToContext(Object o);
+    boolean isVerifyEnabled(DebugContext.Scope scope);
 
     /**
-     * Removes an object the context used by this configuration to do filtering.
-     *
-     * This should only removes extra context added by {@link #addToContext(Object)}.
+     * @see DebugContext#isVerifyEnabledForMethod()
      */
-    void removeFromContext(Object o);
+    boolean isVerifyEnabledForMethod(DebugContext.Scope scope);
 
     /**
-     * @see Debug#timer(CharSequence)
+     * @see DebugContext#timer(CharSequence)
      */
-    boolean isTimeEnabled();
+    boolean isTimeEnabled(DebugContext.Scope scope);
 
     /**
      * Handles notification of an exception occurring within a debug scope.
@@ -112,18 +94,47 @@
      * @return the exception object that is to be propagated to parent scope. A value of
      *         {@code null} indicates that {@code e} is to be propagated.
      */
-    RuntimeException interceptException(Throwable e);
+    RuntimeException interceptException(DebugContext debug, Throwable e);
 
     /**
-     * Gets the modifiable collection of dump handlers registered with this configuration.
+     * Gets an unmodifiable view of the dump handlers registered with this configuration.
      */
     Collection<DebugDumpHandler> dumpHandlers();
 
+    /**
+     * Gets the {@link PrintStream} for logging.
+     */
     PrintStream output();
 
     /**
-     * Gets the modifiable collection of verify handlers registered with this configuration.
+     * Gets an unmodifiable view of the verify handlers registered with this configuration.
      */
     Collection<DebugVerifyHandler> verifyHandlers();
 
+    default void closeDumpHandlers(boolean ignoreErrors) {
+        for (DebugDumpHandler handler : dumpHandlers()) {
+            try {
+                handler.close();
+            } catch (Throwable e) {
+                if (!ignoreErrors) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Extracts a {@link JavaMethod} from an opaque debug context.
+     *
+     * @return the {@link JavaMethod} represented by {@code context} or null
+     */
+    static JavaMethod asJavaMethod(Object context) {
+        if (context instanceof JavaMethodContext) {
+            return ((JavaMethodContext) context).asJavaMethod();
+        }
+        if (context instanceof JavaMethod) {
+            return (JavaMethod) context;
+        }
+        return null;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigCustomizer.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-public interface DebugConfigCustomizer {
-    void customize(DebugConfig config);
-}
--- /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/DebugConfigImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import static org.graalvm.compiler.debug.DebugContext.BASIC_LEVEL;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.graalvm.compiler.options.OptionValues;
+
+import jdk.vm.ci.code.BailoutException;
+import jdk.vm.ci.meta.JavaMethod;
+
+final class DebugConfigImpl implements DebugConfig {
+
+    private final OptionValues options;
+
+    private final DebugFilter countFilter;
+    private final DebugFilter logFilter;
+    private final DebugFilter trackMemUseFilter;
+    private final DebugFilter timerFilter;
+    private final DebugFilter dumpFilter;
+    private final DebugFilter verifyFilter;
+    private final MethodFilter[] methodFilter;
+    private final List<DebugDumpHandler> dumpHandlers;
+    private final List<DebugVerifyHandler> verifyHandlers;
+    private final PrintStream output;
+
+    DebugConfigImpl(OptionValues options) {
+        this(options, TTY.out, Collections.emptyList(), Collections.emptyList());
+    }
+
+    DebugConfigImpl(OptionValues options, PrintStream output,
+                    List<DebugDumpHandler> dumpHandlers,
+                    List<DebugVerifyHandler> verifyHandlers) {
+        this(options, DebugOptions.Log.getValue(options),
+                        DebugOptions.Count.getValue(options),
+                        DebugOptions.TrackMemUse.getValue(options),
+                        DebugOptions.Time.getValue(options),
+                        DebugOptions.Dump.getValue(options),
+                        DebugOptions.Verify.getValue(options),
+                        DebugOptions.MethodFilter.getValue(options),
+                        output, dumpHandlers, verifyHandlers);
+    }
+
+    DebugConfigImpl(OptionValues options,
+                    String logFilter,
+                    String countFilter,
+                    String trackMemUseFilter,
+                    String timerFilter,
+                    String dumpFilter,
+                    String verifyFilter,
+                    String methodFilter,
+                    PrintStream output,
+                    List<DebugDumpHandler> dumpHandlers,
+                    List<DebugVerifyHandler> verifyHandlers) {
+        this.options = options;
+        this.logFilter = DebugFilter.parse(logFilter);
+        this.countFilter = DebugFilter.parse(countFilter);
+        this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter);
+        this.timerFilter = DebugFilter.parse(timerFilter);
+        this.dumpFilter = DebugFilter.parse(dumpFilter);
+        this.verifyFilter = DebugFilter.parse(verifyFilter);
+        if (methodFilter == null || methodFilter.isEmpty()) {
+            this.methodFilter = null;
+        } else {
+            this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter);
+        }
+
+        this.dumpHandlers = Collections.unmodifiableList(dumpHandlers);
+        this.verifyHandlers = Collections.unmodifiableList(verifyHandlers);
+        this.output = output;
+    }
+
+    @Override
+    public OptionValues getOptions() {
+        return options;
+    }
+
+    @Override
+    public int getLogLevel(DebugContext.Scope scope) {
+        return getLevel(scope, logFilter);
+    }
+
+    @Override
+    public boolean isLogEnabledForMethod(DebugContext.Scope scope) {
+        return isEnabledForMethod(scope, logFilter);
+    }
+
+    @Override
+    public boolean isCountEnabled(DebugContext.Scope scope) {
+        return isEnabled(scope, countFilter);
+    }
+
+    @Override
+    public boolean isMemUseTrackingEnabled(DebugContext.Scope scope) {
+        return isEnabled(scope, trackMemUseFilter);
+    }
+
+    @Override
+    public int getDumpLevel(DebugContext.Scope scope) {
+        return getLevel(scope, dumpFilter);
+    }
+
+    @Override
+    public boolean isDumpEnabledForMethod(DebugContext.Scope scope) {
+        return isEnabledForMethod(scope, dumpFilter);
+    }
+
+    @Override
+    public boolean isVerifyEnabled(DebugContext.Scope scope) {
+        return isEnabled(scope, verifyFilter);
+    }
+
+    @Override
+    public boolean isVerifyEnabledForMethod(DebugContext.Scope scope) {
+        return isEnabledForMethod(scope, verifyFilter);
+    }
+
+    @Override
+    public boolean isTimeEnabled(DebugContext.Scope scope) {
+        return isEnabled(scope, timerFilter);
+    }
+
+    @Override
+    public PrintStream output() {
+        return output;
+    }
+
+    private boolean isEnabled(DebugContext.Scope scope, DebugFilter filter) {
+        return getLevel(scope, filter) > 0;
+    }
+
+    private int getLevel(DebugContext.Scope scope, DebugFilter filter) {
+        int level;
+        if (filter == null) {
+            level = 0;
+        } else {
+            String currentScope = scope.getQualifiedName();
+            level = filter.matchLevel(currentScope);
+        }
+        if (level >= 0 && !checkMethodFilter(scope)) {
+            level = -1;
+        }
+        return level;
+    }
+
+    private boolean isEnabledForMethod(DebugContext.Scope scope, DebugFilter filter) {
+        return filter != null && checkMethodFilter(scope);
+    }
+
+    private boolean checkMethodFilter(DebugContext.Scope scope) {
+        if (methodFilter == null) {
+            return true;
+        } else {
+            JavaMethod lastMethod = null;
+            Iterable<Object> context = scope.getCurrentContext();
+            for (Object o : context) {
+                if (methodFilter != null) {
+                    JavaMethod method = DebugConfig.asJavaMethod(o);
+                    if (method != null) {
+                        if (!DebugOptions.MethodFilterRootOnly.getValue(options)) {
+                            if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) {
+                                return true;
+                            }
+                        } else {
+                            /*
+                             * The context values operate as a stack so if we want MethodFilter to
+                             * only apply to the root method we have to check only the last method
+                             * seen.
+                             */
+                            lastMethod = method;
+                        }
+                    }
+                }
+            }
+            if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Debug config:");
+        add(sb, "Log", logFilter);
+        add(sb, "Count", countFilter);
+        add(sb, "Time", timerFilter);
+        add(sb, "Dump", dumpFilter);
+        add(sb, "MethodFilter", methodFilter);
+        return sb.toString();
+    }
+
+    private static void add(StringBuilder sb, String name, Object filter) {
+        if (filter != null) {
+            sb.append(' ');
+            sb.append(name);
+            sb.append('=');
+            if (filter instanceof Object[]) {
+                sb.append(Arrays.toString((Object[]) filter));
+            } else {
+                sb.append(String.valueOf(filter));
+            }
+        }
+    }
+
+    @Override
+    public RuntimeException interceptException(DebugContext debug, Throwable e) {
+        if (e instanceof BailoutException && !DebugOptions.InterceptBailout.getValue(options)) {
+            return null;
+        }
+
+        OptionValues interceptOptions = new OptionValues(options,
+                        DebugOptions.Count, null,
+                        DebugOptions.Time, null,
+                        DebugOptions.TrackMemUse, null,
+                        DebugOptions.Verify, null,
+                        DebugOptions.Dump, ":" + BASIC_LEVEL,
+                        DebugOptions.Log, ":" + BASIC_LEVEL);
+        DebugConfigImpl config = new DebugConfigImpl(interceptOptions, output, dumpHandlers, verifyHandlers);
+        ScopeImpl scope = debug.currentScope;
+        scope.updateFlags(config);
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            e.printStackTrace(new PrintStream(baos));
+            debug.log("Exception raised in scope %s: %s", debug.getCurrentScopeName(), baos);
+            Map<Object, Object> firstSeen = new IdentityHashMap<>();
+            for (Object o : debug.context()) {
+                // Only dump a context object once.
+                if (!firstSeen.containsKey(o)) {
+                    firstSeen.put(o, o);
+                    if (DebugOptions.DumpOnError.getValue(options) || DebugOptions.Dump.getValue(options) != null) {
+                        debug.dump(DebugContext.BASIC_LEVEL, o, "Exception: %s", e);
+                    } else {
+                        debug.log("Context obj %s", o);
+                    }
+                }
+            }
+        } finally {
+            scope.updateFlags(this);
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<DebugDumpHandler> dumpHandlers() {
+        return dumpHandlers;
+    }
+
+    @Override
+    public Collection<DebugVerifyHandler> verifyHandlers() {
+        return verifyHandlers;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigScope.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import org.graalvm.compiler.debug.internal.DebugScope;
-
-/**
- * A utility for scoping a change to the current debug {@linkplain DebugScope#setConfig(DebugConfig)
- * configuration}. For example:
- *
- * <pre>
- *     DebugConfig config = ...;
- *     try (DebugConfigScope s = new DebugConfigScope(config)) {
- *         // ...
- *     }
- * </pre>
- */
-public class DebugConfigScope implements AutoCloseable {
-
-    private final DebugConfig current;
-
-    /**
-     * Sets the current debug {@linkplain DebugScope#setConfig(DebugConfig) configuration} to a
-     * given value and creates an object that when {@linkplain #close() closed} resets the
-     * configuration to the {@linkplain DebugScope#getConfig() current} configuration.
-     */
-    public DebugConfigScope(DebugConfig config) {
-        this.current = DebugScope.getConfig();
-        DebugScope.getInstance().setConfig(config);
-    }
-
-    @Override
-    public void close() {
-        DebugScope.getInstance().setConfig(current);
-    }
-}
--- /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/DebugContext.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,2030 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import static java.util.FormattableFlags.LEFT_JUSTIFY;
+import static java.util.FormattableFlags.UPPERCASE;
+import static org.graalvm.compiler.debug.DebugOptions.Count;
+import static org.graalvm.compiler.debug.DebugOptions.Counters;
+import static org.graalvm.compiler.debug.DebugOptions.Dump;
+import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
+import static org.graalvm.compiler.debug.DebugOptions.DumpOnPhaseChange;
+import static org.graalvm.compiler.debug.DebugOptions.ListMetrics;
+import static org.graalvm.compiler.debug.DebugOptions.Log;
+import static org.graalvm.compiler.debug.DebugOptions.MemUseTrackers;
+import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
+import static org.graalvm.compiler.debug.DebugOptions.Time;
+import static org.graalvm.compiler.debug.DebugOptions.Timers;
+import static org.graalvm.compiler.debug.DebugOptions.TrackMemUse;
+import static org.graalvm.compiler.debug.DebugOptions.Verify;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.EconomicSet;
+import org.graalvm.util.Pair;
+
+import jdk.vm.ci.meta.JavaMethod;
+
+/**
+ * A facility for logging and dumping as well as a container for values associated with
+ * {@link MetricKey}s.
+ *
+ * A {@code DebugContext} object must only be used on the thread that created it. This means it
+ * needs to be passed around as a parameter. For convenience, it can be encapsulated in a widely
+ * used object that is in scope wherever a {@code DebugContext} is needed. However, care must be
+ * taken when such objects can be exposed to multiple threads (e.g., they are in a non-thread-local
+ * cache).
+ */
+public final class DebugContext implements AutoCloseable {
+
+    public static final Description NO_DESCRIPTION = null;
+    public static final GlobalMetrics NO_GLOBAL_METRIC_VALUES = null;
+    public static final Iterable<DebugHandlersFactory> NO_CONFIG_CUSTOMIZERS = Collections.emptyList();
+
+    public static final PrintStream DEFAULT_LOG_STREAM = TTY.out;
+
+    /**
+     * Contains the immutable parts of a debug context. This separation allows the immutable parts
+     * to be shared and reduces the overhead of initialization since most immutable fields are
+     * configured by parsing options.
+     */
+    final Immutable immutable;
+
+    /**
+     * Determines whether metrics are enabled.
+     */
+    boolean metricsEnabled;
+
+    DebugConfig currentConfig;
+    ScopeImpl currentScope;
+    CloseableCounter currentTimer;
+    CloseableCounter currentMemUseTracker;
+    Scope lastClosedScope;
+    Throwable lastExceptionThrown;
+
+    /**
+     * Stores the {@link MetricKey} values.
+     */
+    private long[] metricValues;
+
+    /**
+     * Determines if dynamic scopes are enabled.
+     */
+    public boolean areScopesEnabled() {
+        return immutable.scopesEnabled;
+    }
+
+    /**
+     * The immutable configuration that can be shared between {@link DebugContext} objects.
+     */
+    static final class Immutable {
+
+        private static final Immutable[] CACHE = new Immutable[5];
+
+        /**
+         * The options from which this object was configured.
+         */
+        final OptionValues options;
+
+        /**
+         * Specifies if dynamic scopes are enabled.
+         */
+        final boolean scopesEnabled;
+
+        final boolean listMetrics;
+
+        /**
+         * Names of unscoped counters. A counter is unscoped if this set is empty or contains the
+         * counter's name.
+         */
+        final EconomicSet<String> unscopedCounters;
+
+        /**
+         * Names of unscoped timers. A timer is unscoped if this set is empty or contains the
+         * timer's name.
+         */
+        final EconomicSet<String> unscopedTimers;
+
+        /**
+         * Names of unscoped memory usage trackers. A memory usage tracker is unscoped if this set
+         * is empty or contains the memory usage tracker's name.
+         */
+        final EconomicSet<String> unscopedMemUseTrackers;
+
+        private static EconomicSet<String> parseUnscopedMetricSpec(String spec, boolean unconditional, boolean accumulatedKey) {
+            EconomicSet<String> res;
+            if (spec == null) {
+                if (!unconditional) {
+                    res = null;
+                } else {
+                    res = EconomicSet.create();
+                }
+            } else {
+                res = EconomicSet.create();
+                if (!spec.isEmpty()) {
+                    if (!accumulatedKey) {
+                        res.addAll(Arrays.asList(spec.split(",")));
+                    } else {
+                        for (String n : spec.split(",")) {
+                            res.add(n + AccumulatedKey.ACCUMULATED_KEY_SUFFIX);
+                            res.add(n + AccumulatedKey.FLAT_KEY_SUFFIX);
+                        }
+                    }
+
+                }
+            }
+            return res;
+        }
+
+        static Immutable create(OptionValues options) {
+            int i = 0;
+            while (i < CACHE.length) {
+                Immutable immutable = CACHE[i];
+                if (immutable == null) {
+                    break;
+                }
+                if (immutable.options == options) {
+                    return immutable;
+                }
+                i++;
+            }
+            Immutable immutable = new Immutable(options);
+            if (i < CACHE.length) {
+                CACHE[i] = immutable;
+            }
+            return immutable;
+        }
+
+        private static boolean isNotEmpty(OptionKey<String> option, OptionValues options) {
+            return option.getValue(options) != null && !option.getValue(options).isEmpty();
+        }
+
+        private Immutable(OptionValues options) {
+            this.options = options;
+            this.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false);
+            this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(Time.getValue(options)), true);
+            this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(TrackMemUse.getValue(options)), true);
+
+            this.scopesEnabled = DumpOnError.getValue(options) ||
+                            Dump.getValue(options) != null ||
+                            Log.getValue(options) != null ||
+                            isNotEmpty(DebugOptions.Count, options) ||
+                            isNotEmpty(DebugOptions.Time, options) ||
+                            isNotEmpty(DebugOptions.TrackMemUse, options) ||
+                            DumpOnPhaseChange.getValue(options) != null;
+            this.listMetrics = ListMetrics.getValue(options);
+        }
+
+        private Immutable() {
+            this.options = new OptionValues(EconomicMap.create());
+            this.unscopedCounters = null;
+            this.unscopedTimers = null;
+            this.unscopedMemUseTrackers = null;
+            this.scopesEnabled = false;
+            this.listMetrics = false;
+        }
+
+        public boolean hasUnscopedMetrics() {
+            return unscopedCounters != null || unscopedTimers != null || unscopedMemUseTrackers != null;
+        }
+    }
+
+    /**
+     * Gets the options this debug context was constructed with.
+     */
+    public OptionValues getOptions() {
+        return immutable.options;
+    }
+
+    static class Activated extends ThreadLocal<DebugContext> {
+    }
+
+    private static final Activated activated = new Activated();
+
+    /**
+     * An object used to undo the changes made by DebugContext#activate().
+     */
+    public static class Activation implements AutoCloseable {
+        private final DebugContext parent;
+
+        Activation(DebugContext parent) {
+            this.parent = parent;
+        }
+
+        @Override
+        public void close() {
+            activated.set(parent);
+        }
+    }
+
+    /**
+     * Activates this object as the debug context {@linkplain DebugContext#forCurrentThread for the
+     * current thread}. This method should be used in a try-with-resources statement.
+     *
+     * @return an object that will deactivate the debug context for the current thread when
+     *         {@link Activation#close()} is called on it
+     */
+    public Activation activate() {
+        Activation res = new Activation(activated.get());
+        activated.set(this);
+        return res;
+    }
+
+    /**
+     * Shared object used to represent a disabled debug context.
+     */
+    public static final DebugContext DISABLED = new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, new Immutable(), NO_CONFIG_CUSTOMIZERS);
+
+    /**
+     * Gets the debug context for the current thread. This should only be used when there is no
+     * other reasonable means to get a hold of a debug context.
+     */
+    public static DebugContext forCurrentThread() {
+        DebugContext current = activated.get();
+        if (current == null) {
+            return DISABLED;
+        }
+        return current;
+    }
+
+    private final GlobalMetrics globalMetrics;
+
+    /**
+     * Describes the computation associated with a {@link DebugContext}.
+     */
+    public static class Description {
+        /**
+         * The primary input to the computation.
+         */
+        final Object compilable;
+
+        /**
+         * A runtime based identifier that is most likely to be unique.
+         */
+        final String identifier;
+
+        public Description(Object compilable, String identifier) {
+            this.compilable = compilable;
+            this.identifier = identifier;
+        }
+
+        @Override
+        public String toString() {
+            String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
+            return identifier + ":" + compilableName;
+        }
+    }
+
+    private final Description description;
+
+    /**
+     * Gets a description of the computation associated with this debug context.
+     *
+     * @return {@code null} if no description is available
+     */
+    public Description getDescription() {
+        return description;
+    }
+
+    /**
+     * Gets the global metrics associated with this debug context.
+     *
+     * @return {@code null} if no global metrics are available
+     */
+    public GlobalMetrics getGlobalMetrics() {
+        return globalMetrics;
+    }
+
+    /**
+     * Creates a {@link DebugContext} based on a given set of option values and {@code factory}.
+     */
+    public static DebugContext create(OptionValues options, DebugHandlersFactory factory) {
+        return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), Collections.singletonList(factory));
+    }
+
+    /**
+     * Creates a {@link DebugContext} based on a given set of option values and {@code factories}.
+     * The {@link DebugHandlersFactory#LOADER} can be used for the latter.
+     */
+    public static DebugContext create(OptionValues options, Iterable<DebugHandlersFactory> factories) {
+        return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), factories);
+    }
+
+    /**
+     * Creates a {@link DebugContext}.
+     */
+    public static DebugContext create(OptionValues options, Description description, GlobalMetrics globalMetrics, PrintStream logStream, Iterable<DebugHandlersFactory> factories) {
+        return new DebugContext(description, globalMetrics, logStream, Immutable.create(options), factories);
+    }
+
+    private DebugContext(Description description, GlobalMetrics globalMetrics, PrintStream logStream, Immutable immutable, Iterable<DebugHandlersFactory> factories) {
+        this.immutable = immutable;
+        this.description = description;
+        this.globalMetrics = globalMetrics;
+        if (immutable.scopesEnabled) {
+            OptionValues options = immutable.options;
+            List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
+            List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
+            for (DebugHandlersFactory factory : factories) {
+                for (DebugHandler handler : factory.createHandlers(options)) {
+                    if (handler instanceof DebugDumpHandler) {
+                        dumpHandlers.add((DebugDumpHandler) handler);
+                    } else {
+                        assert handler instanceof DebugVerifyHandler;
+                        verifyHandlers.add((DebugVerifyHandler) handler);
+                    }
+                }
+            }
+            currentConfig = new DebugConfigImpl(
+                            options,
+                            Log.getValue(options),
+                            Count.getValue(options),
+                            TrackMemUse.getValue(options),
+                            Time.getValue(options),
+                            Dump.getValue(options),
+                            Verify.getValue(options),
+                            MethodFilter.getValue(options),
+                            logStream, dumpHandlers, verifyHandlers);
+            currentScope = new ScopeImpl(this, Thread.currentThread());
+            currentScope.updateFlags(currentConfig);
+            metricsEnabled = true;
+        } else {
+            metricsEnabled = immutable.hasUnscopedMetrics() || immutable.listMetrics;
+        }
+    }
+
+    /**
+     * A special dump level that indicates the dumping machinery is enabled but no dumps will be
+     * produced except through other options.
+     */
+    public static final int ENABLED_LEVEL = 0;
+
+    /**
+     * Basic debug level.
+     *
+     * For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
+     * after mid tier, after low tier.
+     *
+     * LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation
+     * stage, and after code installation.
+     */
+    public static final int BASIC_LEVEL = 1;
+
+    /**
+     * Informational debug level.
+     *
+     * HIR dumping: One graph after each applied top-level phase.
+     *
+     * LIR dumping: After each applied phase.
+     */
+    public static final int INFO_LEVEL = 2;
+
+    /**
+     * Verbose debug level.
+     *
+     * HIR dumping: One graph after each phase (including sub phases).
+     *
+     * LIR dumping: 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.
+     *
+     * LIR dumping: Dump CFG within phases where interesting.
+     */
+    public static final int DETAILED_LEVEL = 4;
+
+    /**
+     * Very detailed debug level.
+     *
+     * HIR dumping: Graphs per node granularity graph change (before/after change).
+     *
+     * LIR dumping: Intermediate CFGs of phases where interesting.
+     */
+    public static final int VERY_DETAILED_LEVEL = 5;
+
+    public boolean isDumpEnabled(int dumpLevel) {
+        return currentScope != null && currentScope.isDumpEnabled(dumpLevel);
+    }
+
+    /**
+     * Determines if verification is enabled for any {@link JavaMethod} in the current scope.
+     *
+     * @see DebugContext#verify(Object, String)
+     */
+    public boolean isVerifyEnabledForMethod() {
+        if (currentScope == null) {
+            return false;
+        }
+        if (currentConfig == null) {
+            return false;
+        }
+        return currentConfig.isVerifyEnabledForMethod(currentScope);
+    }
+
+    /**
+     * Determines if verification is enabled in the current scope.
+     *
+     * @see DebugContext#verify(Object, String)
+     */
+    public boolean isVerifyEnabled() {
+        return currentScope != null && currentScope.isVerifyEnabled();
+    }
+
+    public boolean isCountEnabled() {
+        return currentScope != null && currentScope.isCountEnabled();
+    }
+
+    public boolean isTimeEnabled() {
+        return currentScope != null && currentScope.isTimeEnabled();
+    }
+
+    public boolean isMemUseTrackingEnabled() {
+        return currentScope != null && currentScope.isMemUseTrackingEnabled();
+    }
+
+    public boolean isDumpEnabledForMethod() {
+        if (currentConfig == null) {
+            return false;
+        }
+        return currentConfig.isDumpEnabledForMethod(currentScope);
+    }
+
+    public boolean isLogEnabledForMethod() {
+        if (currentScope == null) {
+            return false;
+        }
+        if (currentConfig == null) {
+            return false;
+        }
+        return currentConfig.isLogEnabledForMethod(currentScope);
+    }
+
+    public boolean isLogEnabled() {
+        return currentScope != null && isLogEnabled(BASIC_LEVEL);
+    }
+
+    public boolean isLogEnabled(int logLevel) {
+        return currentScope != null && currentScope.isLogEnabled(logLevel);
+    }
+
+    /**
+     * Gets a string composed of the names in the current nesting of debug
+     * {@linkplain #scope(Object) scopes} separated by {@code '.'}.
+     */
+    public String getCurrentScopeName() {
+        if (currentScope != null) {
+            return currentScope.getQualifiedName();
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * Creates and enters a new debug scope which will be a child of the current debug scope.
+     * <p>
+     * It is recommended to use the try-with-resource statement for managing entering and leaving
+     * debug scopes. For example:
+     *
+     * <pre>
+     * try (Scope s = Debug.scope(&quot;InliningGraph&quot;, inlineeGraph)) {
+     *     ...
+     * } catch (Throwable e) {
+     *     throw Debug.handle(e);
+     * }
+     * </pre>
+     *
+     * The {@code name} argument is subject to the following type based conversion before having
+     * {@link Object#toString()} called on it:
+     *
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     *
+     * @param name the name of the new scope
+     * @param contextObjects an array of object to be appended to the {@linkplain #context()
+     *            current} debug context
+     * @throws Throwable used to enforce a catch block.
+     * @return the scope entered by this method which will be exited when its {@link Scope#close()}
+     *         method is called
+     */
+    public DebugContext.Scope scope(Object name, Object[] contextObjects) throws Throwable {
+        if (currentScope != null) {
+            return enterScope(convertFormatArg(name).toString(), null, contextObjects);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
+     * block can be omitted.
+     *
+     * @see #scope(Object, Object[])
+     */
+    public DebugContext.Scope scope(Object name) {
+        if (currentScope != null) {
+            return enterScope(convertFormatArg(name).toString(), null);
+        } else {
+            return null;
+        }
+    }
+
+    private final Invariants invariants = Assertions.ENABLED ? new Invariants() : null;
+
+    static StackTraceElement[] getStackTrace(Thread thread) {
+        return thread.getStackTrace();
+    }
+
+    /**
+     * Utility for enforcing {@link DebugContext} invariants via assertions.
+     */
+    static class Invariants {
+        private final Thread thread;
+        private final StackTraceElement[] origin;
+
+        Invariants() {
+            thread = Thread.currentThread();
+            origin = getStackTrace(thread);
+        }
+
+        boolean checkNoConcurrentAccess() {
+            Thread currentThread = Thread.currentThread();
+            if (currentThread != thread) {
+                Formatter buf = new Formatter();
+                buf.format("Thread local %s object was created on thread %s but is being accessed by thread %s. The most likely cause is " +
+                                "that the object is being retrieved from a non-thread-local cache.",
+                                DebugContext.class.getName(), thread, currentThread);
+                int debugContextConstructors = 0;
+                boolean addedHeader = false;
+                for (StackTraceElement e : origin) {
+                    if (e.getMethodName().equals("<init>") && e.getClassName().equals(DebugContext.class.getName())) {
+                        debugContextConstructors++;
+                    } else if (debugContextConstructors != 0) {
+                        if (!addedHeader) {
+                            addedHeader = true;
+                            buf.format(" The object was instantiated here:");
+                        }
+                        // Distinguish from assertion stack trace by using double indent and
+                        // "in" instead of "at" prefix.
+                        buf.format("%n\t\tin %s", e);
+                    }
+                }
+                if (addedHeader) {
+                    buf.format("%n");
+                }
+
+                throw new AssertionError(buf.toString());
+            }
+            return true;
+        }
+    }
+
+    boolean checkNoConcurrentAccess() {
+        assert invariants == null || invariants.checkNoConcurrentAccess();
+        return true;
+    }
+
+    private DebugContext.Scope enterScope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
+        assert checkNoConcurrentAccess();
+        currentScope = currentScope.scope(name, sandboxConfig, newContextObjects);
+        return currentScope;
+    }
+
+    /**
+     * @see #scope(Object, Object[])
+     * @param context an object to be appended to the {@linkplain #context() current} debug context
+     */
+    public DebugContext.Scope scope(Object name, Object context) throws Throwable {
+        if (currentScope != null) {
+            return enterScope(convertFormatArg(name).toString(), null, context);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @see #scope(Object, Object[])
+     * @param context1 first object to be appended to the {@linkplain #context() current} debug
+     *            context
+     * @param context2 second object to be appended to the {@linkplain #context() current} debug
+     *            context
+     */
+    public DebugContext.Scope scope(Object name, Object context1, Object context2) throws Throwable {
+        if (currentScope != null) {
+            return enterScope(convertFormatArg(name).toString(), null, context1, context2);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @see #scope(Object, Object[])
+     * @param context1 first object to be appended to the {@linkplain #context() current} debug
+     *            context
+     * @param context2 second object to be appended to the {@linkplain #context() current} debug
+     *            context
+     * @param context3 third object to be appended to the {@linkplain #context() current} debug
+     *            context
+     */
+    public DebugContext.Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
+        if (currentScope != null) {
+            return enterScope(convertFormatArg(name).toString(), null, context1, context2, context3);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Creates and enters a new debug scope which will be disjoint from the current debug scope.
+     * <p>
+     * It is recommended to use the try-with-resource statement for managing entering and leaving
+     * debug scopes. For example:
+     *
+     * <pre>
+     * try (Scope s = Debug.sandbox(&quot;CompilingStub&quot;, null, stubGraph)) {
+     *     ...
+     * } catch (Throwable e) {
+     *     throw Debug.handle(e);
+     * }
+     * </pre>
+     *
+     * @param name the name of the new scope
+     * @param config the debug configuration to use for the new scope or {@code null} to disable the
+     *            scoping mechanism within the sandbox scope
+     * @param context objects to be appended to the {@linkplain #context() current} debug context
+     * @return the scope entered by this method which will be exited when its {@link Scope#close()}
+     *         method is called
+     */
+    public DebugContext.Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
+        if (config == null) {
+            return disable();
+        }
+        if (currentScope != null) {
+            return enterScope(name, config, context);
+        } else {
+            return null;
+        }
+    }
+
+    class DisabledScope implements DebugContext.Scope {
+        final boolean savedMetricsEnabled;
+        final ScopeImpl savedScope;
+        final DebugConfig savedConfig;
+
+        DisabledScope() {
+            this.savedMetricsEnabled = metricsEnabled;
+            this.savedScope = currentScope;
+            this.savedConfig = currentConfig;
+            metricsEnabled = false;
+            currentScope = null;
+            currentConfig = null;
+        }
+
+        @Override
+        public String getQualifiedName() {
+            return "";
+        }
+
+        @Override
+        public Iterable<Object> getCurrentContext() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public void close() {
+            metricsEnabled = savedMetricsEnabled;
+            currentScope = savedScope;
+            currentConfig = savedConfig;
+            lastClosedScope = this;
+        }
+    }
+
+    /**
+     * Disables all metrics and scope related functionality until {@code close()} is called on the
+     * returned object.
+     */
+    public DebugContext.Scope disable() {
+        if (currentScope != null) {
+            return new DisabledScope();
+        } else {
+            return null;
+        }
+    }
+
+    public DebugContext.Scope forceLog() throws Throwable {
+        if (currentConfig != null) {
+            ArrayList<Object> context = new ArrayList<>();
+            for (Object obj : context()) {
+                context.add(obj);
+            }
+            DebugConfigImpl config = new DebugConfigImpl(new OptionValues(currentConfig.getOptions(), DebugOptions.Log, ":1000"));
+            return sandbox("forceLog", config, context.toArray());
+        }
+        return null;
+    }
+
+    /**
+     * Opens a scope in which exception
+     * {@linkplain DebugConfig#interceptException(DebugContext, Throwable) interception} is
+     * disabled. The current state of interception is restored when {@link DebugCloseable#close()}
+     * is called on the returned object.
+     *
+     * This is particularly useful to suppress extraneous output in JUnit tests that are expected to
+     * throw an exception.
+     */
+    public DebugCloseable disableIntercept() {
+        if (currentScope != null) {
+            return currentScope.disableIntercept();
+        }
+        return null;
+    }
+
+    /**
+     * Handles an exception in the context of the debug scope just exited. The just exited scope
+     * must have the current scope as its parent which will be the case if the try-with-resource
+     * pattern recommended by {@link #scope(Object)} and
+     * {@link #sandbox(CharSequence, DebugConfig, Object...)} is used
+     *
+     * @see #scope(Object, Object[])
+     * @see #sandbox(CharSequence, DebugConfig, Object...)
+     */
+    public RuntimeException handle(Throwable exception) {
+        if (currentScope != null) {
+            return currentScope.handle(exception);
+        } else {
+            if (exception instanceof Error) {
+                throw (Error) exception;
+            }
+            if (exception instanceof RuntimeException) {
+                throw (RuntimeException) exception;
+            }
+            throw new RuntimeException(exception);
+        }
+    }
+
+    public void log(String msg) {
+        log(BASIC_LEVEL, msg);
+    }
+
+    /**
+     * Prints a message to the current debug scope's logging stream if logging is enabled.
+     *
+     * @param msg the message to log
+     */
+    public void log(int logLevel, String msg) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, msg);
+        }
+    }
+
+    public void log(String format, Object arg) {
+        log(BASIC_LEVEL, format, arg);
+    }
+
+    /**
+     * Prints a message to the current debug scope's logging stream if logging is enabled.
+     *
+     * @param format a format string
+     * @param arg the argument referenced by the format specifiers in {@code format}
+     */
+    public void log(int logLevel, String format, Object arg) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg);
+        }
+    }
+
+    public void log(String format, int arg) {
+        log(BASIC_LEVEL, format, arg);
+    }
+
+    /**
+     * Prints a message to the current debug scope's logging stream if logging is enabled.
+     *
+     * @param format a format string
+     * @param arg the argument referenced by the format specifiers in {@code format}
+     */
+    public void log(int logLevel, String format, int arg) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2) {
+        log(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2);
+        }
+    }
+
+    public void log(String format, int arg1, Object arg2) {
+        log(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, int arg1, Object arg2) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2);
+        }
+    }
+
+    public void log(String format, Object arg1, int arg2) {
+        log(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, int arg2) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2);
+        }
+    }
+
+    public void log(String format, int arg1, int arg2) {
+        log(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, int arg1, int arg2) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3);
+        }
+    }
+
+    public void log(String format, int arg1, int arg2, int arg3) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, int arg1, int arg2, int arg3) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+    }
+
+    /**
+     * @see #log(int, String, Object)
+     */
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+        }
+    }
+
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+        }
+    }
+
+    public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
+        log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+    }
+
+    public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+        }
+    }
+
+    public 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_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+    }
+
+    public 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) {
+        if (currentScope != null) {
+            currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+        }
+    }
+
+    public void logv(String format, Object... args) {
+        logv(BASIC_LEVEL, format, args);
+    }
+
+    /**
+     * Prints a message to the current debug scope's logging stream. This method must only be called
+     * if debugging scopes are {@linkplain DebugContext#areScopesEnabled() enabled} as it incurs
+     * allocation at the call site. If possible, call one of the other {@code log()} methods in this
+     * class that take a fixed number of parameters.
+     *
+     * @param format a format string
+     * @param args the arguments referenced by the format specifiers in {@code format}
+     */
+    public void logv(int logLevel, String format, Object... args) {
+        if (currentScope == null) {
+            throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()");
+        }
+        currentScope.log(logLevel, format, args);
+    }
+
+    /**
+     * This override exists to catch cases when {@link #log(String, Object)} is called with one
+     * argument bound to a varargs method parameter. It will bind to this method instead of the
+     * single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void log(String format, Object[] args) {
+        assert false : "shouldn't use this";
+        log(BASIC_LEVEL, format, args);
+    }
+
+    /**
+     * This override exists to catch cases when {@link #log(int, String, Object)} is called with one
+     * argument bound to a varargs method parameter. It will bind to this method instead of the
+     * single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void log(int logLevel, String format, Object[] args) {
+        assert false : "shouldn't use this";
+        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 void forceDump(Object object, String format, Object... args) {
+        DebugConfig config = currentConfig;
+        Collection<DebugDumpHandler> dumpHandlers;
+        boolean closeAfterDump;
+        if (config != null) {
+            dumpHandlers = config.dumpHandlers();
+            closeAfterDump = false;
+        } else {
+            OptionValues options = getOptions();
+            dumpHandlers = new ArrayList<>();
+            for (DebugHandlersFactory factory : DebugHandlersFactory.LOADER) {
+                for (DebugHandler handler : factory.createHandlers(options)) {
+                    if (handler instanceof DebugDumpHandler) {
+                        dumpHandlers.add((DebugDumpHandler) handler);
+                    }
+                }
+            }
+            closeAfterDump = true;
+        }
+        for (DebugDumpHandler dumpHandler : dumpHandlers) {
+            dumpHandler.dump(this, object, format, args);
+            if (closeAfterDump) {
+                dumpHandler.close();
+            }
+        }
+    }
+
+    public void dump(int dumpLevel, Object object, String msg) {
+        if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
+            currentScope.dump(dumpLevel, object, msg);
+        }
+    }
+
+    public void dump(int dumpLevel, Object object, String format, Object arg) {
+        if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
+            currentScope.dump(dumpLevel, object, format, arg);
+        }
+    }
+
+    public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) {
+        if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
+            currentScope.dump(dumpLevel, object, format, arg1, arg2);
+        }
+    }
+
+    public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) {
+        if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
+            currentScope.dump(dumpLevel, object, format, arg1, arg2, arg3);
+        }
+    }
+
+    /**
+     * This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called
+     * with one argument bound to a varargs method parameter. It will bind to this method instead of
+     * the single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void dump(int dumpLevel, Object object, String format, Object[] args) {
+        assert false : "shouldn't use this";
+        if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
+            currentScope.dump(dumpLevel, object, format, args);
+        }
+    }
+
+    /**
+     * Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
+     * perform verification on a given object.
+     *
+     * @param object object to verify
+     * @param message description of verification context
+     *
+     * @see DebugVerifyHandler#verify
+     */
+    public void verify(Object object, String message) {
+        if (currentScope != null && currentScope.isVerifyEnabled()) {
+            currentScope.verify(object, message);
+        }
+    }
+
+    /**
+     * Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
+     * perform verification on a given object.
+     *
+     * @param object object to verify
+     * @param format a format string for the description of the verification context
+     * @param arg the argument referenced by the format specifiers in {@code format}
+     *
+     * @see DebugVerifyHandler#verify
+     */
+    public void verify(Object object, String format, Object arg) {
+        if (currentScope != null && currentScope.isVerifyEnabled()) {
+            currentScope.verify(object, format, arg);
+        }
+    }
+
+    /**
+     * This override exists to catch cases when {@link #verify(Object, String, Object)} is called
+     * with one argument bound to a varargs method parameter. It will bind to this method instead of
+     * the single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void verify(Object object, String format, Object[] args) {
+        assert false : "shouldn't use this";
+        if (currentScope != null && currentScope.isVerifyEnabled()) {
+            currentScope.verify(object, format, args);
+        }
+    }
+
+    /**
+     * Opens a new indentation level (by adding some spaces) based on the current indentation level.
+     * This should be used in a {@linkplain Indent try-with-resources} pattern.
+     *
+     * @return an object that reverts to the current indentation level when
+     *         {@linkplain Indent#close() closed} or null if debugging is disabled
+     * @see #logAndIndent(int, String)
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent indent() {
+        if (currentScope != null) {
+            return currentScope.pushIndentLogger();
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String msg) {
+        return logAndIndent(BASIC_LEVEL, msg);
+    }
+
+    /**
+     * A convenience function which combines {@link #log(String)} and {@link #indent()}.
+     *
+     * @param msg the message to log
+     * @return an object that reverts to the current indentation level when
+     *         {@linkplain Indent#close() closed} or null if debugging is disabled
+     */
+    public Indent logAndIndent(int logLevel, String msg) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, msg);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg) {
+        return logAndIndent(BASIC_LEVEL, format, arg);
+    }
+
+    /**
+     * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
+     *
+     * @param format a format string
+     * @param arg the argument referenced by the format specifiers in {@code format}
+     * @return an object that reverts to the current indentation level when
+     *         {@linkplain Indent#close() closed} or null if debugging is disabled
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, int arg) {
+        return logAndIndent(BASIC_LEVEL, format, arg);
+    }
+
+    /**
+     * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
+     *
+     * @param format a format string
+     * @param arg the argument referenced by the format specifiers in {@code format}
+     * @return an object that reverts to the current indentation level when
+     *         {@linkplain Indent#close() closed} or null if debugging is disabled
+     */
+    public Indent logAndIndent(int logLevel, String format, int arg) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, int arg1, Object arg2) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, int arg2) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, int arg1, int arg2) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, int arg1, int arg2) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, Object arg2) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5);
+        }
+        return null;
+    }
+
+    public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
+        return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
+    }
+
+    /**
+     * @see #logAndIndent(int, String, Object)
+     */
+    public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
+        if (currentScope != null && isLogEnabled(logLevel)) {
+            return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
+        }
+        return null;
+    }
+
+    /**
+     * A convenience function which combines {@link #logv(int, String, Object...)} and
+     * {@link #indent()}.
+     *
+     * @param format a format string
+     * @param args the arguments referenced by the format specifiers in {@code format}
+     * @return an object that reverts to the current indentation level when
+     *         {@linkplain Indent#close() closed} or null if debugging is disabled
+     */
+    public Indent logvAndIndent(int logLevel, String format, Object... args) {
+        if (currentScope != null) {
+            if (isLogEnabled(logLevel)) {
+                return logvAndIndentInternal(logLevel, format, args);
+            }
+            return null;
+        }
+        throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()");
+    }
+
+    private Indent logvAndIndentInternal(int logLevel, String format, Object... args) {
+        assert currentScope != null && isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()";
+        currentScope.log(logLevel, format, args);
+        return currentScope.pushIndentLogger();
+    }
+
+    /**
+     * This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with
+     * one argument bound to a varargs method parameter. It will bind to this method instead of the
+     * single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void logAndIndent(String format, Object[] args) {
+        assert false : "shouldn't use this";
+        logAndIndent(BASIC_LEVEL, format, args);
+    }
+
+    /**
+     * This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called
+     * with one argument bound to a varargs method parameter. It will bind to this method instead of
+     * the single arg variant and produce a deprecation warning instead of silently wrapping the
+     * Object[] inside of another Object[].
+     */
+    @Deprecated
+    public void logAndIndent(int logLevel, String format, Object[] args) {
+        assert false : "shouldn't use this";
+        logvAndIndent(logLevel, format, args);
+    }
+
+    public Iterable<Object> context() {
+        if (currentScope != null) {
+            return currentScope.getCurrentContext();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> List<T> contextSnapshot(Class<T> clazz) {
+        if (currentScope != null) {
+            List<T> result = new ArrayList<>();
+            for (Object o : context()) {
+                if (clazz.isInstance(o)) {
+                    result.add((T) o);
+                }
+            }
+            return result;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Searches the current debug scope, bottom up, for a context object that is an instance of a
+     * given type. The first such object found is returned.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T contextLookup(Class<T> clazz) {
+        if (currentScope != null) {
+            for (Object o : context()) {
+                if (clazz.isInstance(o)) {
+                    return ((T) o);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Searches the current debug scope, top down, for a context object that is an instance of a
+     * given type. The first such object found is returned.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T contextLookupTopdown(Class<T> clazz) {
+        if (currentScope != null) {
+            T found = null;
+            for (Object o : context()) {
+                if (clazz.isInstance(o)) {
+                    found = (T) o;
+                }
+            }
+            return found;
+        }
+        return null;
+    }
+
+    /**
+     * Creates a {@linkplain MemUseTrackerKey memory use tracker}.
+     */
+    public static MemUseTrackerKey memUseTracker(CharSequence name) {
+        return createMemUseTracker("%s", name, null);
+    }
+
+    /**
+     * Creates a debug memory use tracker. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.memUseTracker(format, arg, null)
+     * </pre>
+     *
+     * except that the string formatting only happens if mem tracking is enabled.
+     *
+     * @see #counter(String, Object, Object)
+     */
+    public static MemUseTrackerKey memUseTracker(String format, Object arg) {
+        return createMemUseTracker(format, arg, null);
+    }
+
+    /**
+     * Creates a debug memory use tracker. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.memUseTracker(String.format(format, arg1, arg2))
+     * </pre>
+     *
+     * except that the string formatting only happens if memory use tracking is enabled. In
+     * addition, each argument is subject to the following type based conversion before being passed
+     * as an argument to {@link String#format(String, Object...)}:
+     *
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     *
+     * @see #memUseTracker(CharSequence)
+     */
+    public static MemUseTrackerKey memUseTracker(String format, Object arg1, Object arg2) {
+        return createMemUseTracker(format, arg1, arg2);
+    }
+
+    private static MemUseTrackerKey createMemUseTracker(String format, Object arg1, Object arg2) {
+        return new MemUseTrackerKeyImpl(format, arg1, arg2);
+    }
+
+    /**
+     * Creates a {@linkplain CounterKey counter}.
+     */
+    public static CounterKey counter(CharSequence name) {
+        return createCounter("%s", name, null);
+    }
+
+    /**
+     * Gets a tally of the metric values in this context and a given tally.
+     *
+     * @param tally the tally to which the metrics should be added
+     * @return a tally of the metric values in this context and {@code tally}. This will be
+     *         {@code tally} if this context has no metric values or {@code tally} is wide enough to
+     *         hold all the metric values in this context otherwise it will be a new array.
+     */
+    public long[] addValuesTo(long[] tally) {
+        if (metricValues == null) {
+            return tally;
+        }
+        if (tally == null) {
+            return metricValues.clone();
+        } else if (metricValues.length >= tally.length) {
+            long[] newTally = metricValues.clone();
+            for (int i = 0; i < tally.length; i++) {
+                newTally[i] += tally[i];
+            }
+            return newTally;
+        } else {
+            for (int i = 0; i < metricValues.length; i++) {
+                tally[i] += metricValues[i];
+            }
+            return tally;
+        }
+    }
+
+    /**
+     * Creates and returns a sorted map from metric names to their values in {@code values}.
+     *
+     * @param values values for metrics in the {@link KeyRegistry}.
+     */
+    public static EconomicMap<MetricKey, Long> convertValuesToKeyValueMap(long[] values) {
+        List<MetricKey> keys = KeyRegistry.getKeys();
+        Collections.sort(keys, MetricKey.NAME_COMPARATOR);
+        EconomicMap<MetricKey, Long> res = EconomicMap.create(keys.size());
+        for (MetricKey key : keys) {
+            int index = ((AbstractKey) key).getIndex();
+            if (index >= values.length) {
+                res.put(key, 0L);
+            } else {
+                res.put(key, values[index]);
+            }
+        }
+        return res;
+    }
+
+    void setMetricValue(int keyIndex, long l) {
+        ensureMetricValuesSize(keyIndex);
+        metricValues[keyIndex] = l;
+    }
+
+    long getMetricValue(int keyIndex) {
+        if (metricValues == null || metricValues.length <= keyIndex) {
+            return 0L;
+        }
+        return metricValues[keyIndex];
+    }
+
+    private void ensureMetricValuesSize(int index) {
+        if (metricValues == null) {
+            metricValues = new long[index + 1];
+        }
+        if (metricValues.length <= index) {
+            metricValues = Arrays.copyOf(metricValues, index + 1);
+        }
+    }
+
+    public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
+        if (flags == 0 && width < 0) {
+            return s;
+        }
+        StringBuilder sb = new StringBuilder(s);
+
+        // apply width and justification
+        int len = sb.length();
+        if (len < width) {
+            for (int i = 0; i < width - len; i++) {
+                if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
+                    sb.append(' ');
+                } else {
+                    sb.insert(0, ' ');
+                }
+            }
+        }
+
+        String res = sb.toString();
+        if ((flags & UPPERCASE) == UPPERCASE) {
+            res = res.toUpperCase();
+        }
+        return res;
+    }
+
+    /**
+     * Creates a debug counter. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.counter(format, arg, null)
+     * </pre>
+     *
+     * except that the string formatting only happens if count is enabled.
+     *
+     * @see #counter(String, Object, Object)
+     */
+    public static CounterKey counter(String format, Object arg) {
+        return createCounter(format, arg, null);
+    }
+
+    /**
+     * Creates a debug counter. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.counter(String.format(format, arg1, arg2))
+     * </pre>
+     *
+     * except that the string formatting only happens if count is enabled. In addition, each
+     * argument is subject to the following type based conversion before being passed as an argument
+     * to {@link String#format(String, Object...)}:
+     *
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     *
+     * @see #counter(CharSequence)
+     */
+    public static CounterKey counter(String format, Object arg1, Object arg2) {
+        return createCounter(format, arg1, arg2);
+    }
+
+    private static CounterKey createCounter(String format, Object arg1, Object arg2) {
+        return new CounterKeyImpl(format, arg1, arg2);
+    }
+
+    public DebugConfig getConfig() {
+        return currentConfig;
+    }
+
+    /**
+     * Creates a {@linkplain TimerKey timer}.
+     * <p>
+     * A disabled timer has virtually no overhead.
+     */
+    public static TimerKey timer(CharSequence name) {
+        return createTimer("%s", name, null);
+    }
+
+    /**
+     * Creates a debug timer. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.timer(format, arg, null)
+     * </pre>
+     *
+     * except that the string formatting only happens if timing is enabled.
+     *
+     * @see #timer(String, Object, Object)
+     */
+    public static TimerKey timer(String format, Object arg) {
+        return createTimer(format, arg, null);
+    }
+
+    /**
+     * Creates a debug timer. Invoking this method is equivalent to:
+     *
+     * <pre>
+     * Debug.timer(String.format(format, arg1, arg2))
+     * </pre>
+     *
+     * except that the string formatting only happens if timing is enabled. In addition, each
+     * argument is subject to the following type based conversion before being passed as an argument
+     * to {@link String#format(String, Object...)}:
+     *
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     *
+     * @see #timer(CharSequence)
+     */
+    public static TimerKey timer(String format, Object arg1, Object arg2) {
+        return createTimer(format, arg1, arg2);
+    }
+
+    /**
+     * There are paths where construction of formatted class names are common and the code below is
+     * surprisingly expensive, so compute it once and cache it.
+     */
+    private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
+        @Override
+        protected String computeValue(Class<?> c) {
+            final String simpleName = c.getSimpleName();
+            Class<?> enclosingClass = c.getEnclosingClass();
+            if (enclosingClass != null) {
+                String prefix = "";
+                while (enclosingClass != null) {
+                    prefix = enclosingClass.getSimpleName() + "_" + prefix;
+                    enclosingClass = enclosingClass.getEnclosingClass();
+                }
+                return prefix + simpleName;
+            } else {
+                return simpleName;
+            }
+        }
+    };
+
+    public static Object convertFormatArg(Object arg) {
+        if (arg instanceof Class) {
+            return formattedClassName.get((Class<?>) arg);
+        }
+        return arg;
+    }
+
+    static String formatDebugName(String format, Object arg1, Object arg2) {
+        return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
+    }
+
+    private static TimerKey createTimer(String format, Object arg1, Object arg2) {
+        return new TimerKeyImpl(format, arg1, arg2);
+    }
+
+    /**
+     * Represents a debug scope entered by {@link DebugContext#scope(Object)} or
+     * {@link DebugContext#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is
+     * achieved via {@link #close()}.
+     */
+    public interface Scope extends AutoCloseable {
+        /**
+         * Gets the names of this scope and its ancestors separated by {@code '.'}.
+         */
+        String getQualifiedName();
+
+        Iterable<Object> getCurrentContext();
+
+        @Override
+        void close();
+    }
+
+    boolean isTimerEnabled(TimerKeyImpl key) {
+        if (!metricsEnabled) {
+            // Pulling this common case out of `isTimerEnabledSlow`
+            // gives C1 a better chance to inline this method.
+            return false;
+        }
+        return isTimerEnabledSlow(key);
+    }
+
+    private boolean isTimerEnabledSlow(AbstractKey key) {
+        if (currentScope != null && currentScope.isTimeEnabled()) {
+            return true;
+        }
+        if (immutable.listMetrics) {
+            key.ensureInitialized();
+        }
+        assert checkNoConcurrentAccess();
+        EconomicSet<String> unscoped = immutable.unscopedTimers;
+        return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
+    }
+
+    /**
+     * Determines if a given timer is enabled in the current scope.
+     */
+    boolean isCounterEnabled(CounterKeyImpl key) {
+        if (!metricsEnabled) {
+            // Pulling this common case out of `isCounterEnabledSlow`
+            // gives C1 a better chance to inline this method.
+            return false;
+        }
+        return isCounterEnabledSlow(key);
+    }
+
+    private boolean isCounterEnabledSlow(AbstractKey key) {
+        if (currentScope != null && currentScope.isCountEnabled()) {
+            return true;
+        }
+        if (immutable.listMetrics) {
+            key.ensureInitialized();
+        }
+        assert checkNoConcurrentAccess();
+        EconomicSet<String> unscoped = immutable.unscopedCounters;
+        return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
+    }
+
+    boolean isMemUseTrackerEnabled(MemUseTrackerKeyImpl key) {
+        if (!metricsEnabled) {
+            // Pulling this common case out of `isMemUseTrackerEnabledSlow`
+            // gives C1 a better chance to inline this method.
+            return false;
+        }
+        return isMemUseTrackerEnabledSlow(key);
+    }
+
+    private boolean isMemUseTrackerEnabledSlow(AbstractKey key) {
+        if (currentScope != null && currentScope.isMemUseTrackingEnabled()) {
+            return true;
+        }
+        if (immutable.listMetrics) {
+            key.ensureInitialized();
+        }
+        assert checkNoConcurrentAccess();
+        EconomicSet<String> unscoped = immutable.unscopedMemUseTrackers;
+        return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
+    }
+
+    public boolean areMetricsEnabled() {
+        return metricsEnabled;
+    }
+
+    @Override
+    public void close() {
+        closeDumpHandlers(false);
+        if (description != null) {
+            printMetrics(description);
+        }
+        if (metricsEnabled && globalMetrics != null && metricValues != null) {
+            globalMetrics.add(this);
+        }
+        metricValues = null;
+    }
+
+    public void closeDumpHandlers(boolean ignoreErrors) {
+        if (currentConfig != null) {
+            currentConfig.closeDumpHandlers(ignoreErrors);
+        }
+    }
+
+    /**
+     * Records how many times a given method has been compiled.
+     */
+    private static EconomicMap<Integer, Integer> compilations;
+
+    /**
+     * Maintains maximum buffer size used by {@link #printMetrics(Description)} to minimize buffer
+     * resizing during subsequent calls to this method.
+     */
+    private static int metricsBufSize = 50_000;
+
+    /**
+     * Flag that allows the first call to {@link #printMetrics(Description)} to delete the file that
+     * will be appended to.
+     */
+    private static boolean metricsFileDeleteCheckPerformed;
+
+    /**
+     * Prints metric values in this object to the file (if any) specified by
+     * {@link DebugOptions#MetricsFile}.
+     */
+    public void printMetrics(Description desc) {
+        if (metricValues == null) {
+            return;
+        }
+        String metricsFile = DebugOptions.MetricsFile.getValue(getOptions());
+        if (metricsFile != null) {
+            // Use identity to distinguish methods that have been redefined
+            // or loaded by different class loaders.
+            Object compilable = desc.compilable;
+            Integer identity = System.identityHashCode(compilable);
+            int compilationNr;
+            synchronized (PRINT_METRICS_LOCK) {
+                if (!metricsFileDeleteCheckPerformed) {
+                    metricsFileDeleteCheckPerformed = true;
+                    File file = new File(metricsFile);
+                    if (file.exists()) {
+                        // This can return false in case something like /dev/stdout
+                        // is specified. If the file is unwriteable, the file open
+                        // below will fail.
+                        file.delete();
+                    }
+                }
+                if (compilations == null) {
+                    compilationNr = 0;
+                    compilations = EconomicMap.create();
+                } else {
+                    Integer value = compilations.get(identity);
+                    compilationNr = value == null ? 0 : value + 1;
+                }
+                compilations.put(identity, compilationNr);
+            }
+
+            // Release the lock while generating the content to reduce contention.
+            // This means `compilationNr` fields may show up out of order in the file.
+            ByteArrayOutputStream baos = new ByteArrayOutputStream(metricsBufSize);
+            PrintStream out = new PrintStream(baos);
+            if (metricsFile.endsWith(".csv") || metricsFile.endsWith(".CSV")) {
+                printMetricsCSV(out, compilable, identity, compilationNr, desc.identifier);
+            } else {
+                printMetrics(out, compilable, identity, compilationNr, desc.identifier);
+            }
+
+            byte[] content = baos.toByteArray();
+            Path path = Paths.get(metricsFile);
+            synchronized (PRINT_METRICS_LOCK) {
+                metricsBufSize = Math.max(metricsBufSize, content.length);
+                try {
+                    Files.write(path, content, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
+                } catch (IOException e) {
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Lock to serialize writes to {@link DebugOptions#MetricsFile}.
+     */
+    private static final Object PRINT_METRICS_LOCK = new Object();
+
+    /**
+     * Appends metrics in CSV format to {@code out} for a single method compilation.
+     *
+     * @param identity the identity hash code of {@code compilable}
+     * @param compilationNr where this compilation lies in the ordered sequence of all compilations
+     *            identified by {@code identity}
+     * @param compilationId the runtime issued identifier for the compilation
+     */
+    private void printMetricsCSV(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
+        String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
+        String csvFormat = CSVUtil.buildFormatString("%s", "%s", "%d", "%s");
+        String format = String.format(csvFormat, CSVUtil.Escape.escapeArgs(compilableName, identity, compilationNr, compilationId));
+        char sep = CSVUtil.SEPARATOR;
+        format += sep + "%s" + sep + "%s" + sep + "%s";
+        for (MetricKey key : KeyRegistry.getKeys()) {
+            int index = ((AbstractKey) key).getIndex();
+            if (index < metricValues.length) {
+                Pair<String, String> valueAndUnit = key.toCSVFormat(metricValues[index]);
+                CSVUtil.Escape.println(out, format, CSVUtil.Escape.escape(key.getName()), valueAndUnit.getLeft(), valueAndUnit.getRight());
+            }
+        }
+    }
+
+    /**
+     * Appends metrics in a human readable format to {@code out} for a single method compilation.
+     *
+     * @param identity the identity hash code of {@code compilable}
+     * @param compilationNr where this compilation lies in the ordered sequence of all compilations
+     *            identified by {@code identity}
+     * @param compilationId the runtime issued identifier for the compilation
+     */
+    private void printMetrics(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
+        String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
+        int maxKeyWidth = compilableName.length();
+        SortedMap<String, String> res = new TreeMap<>();
+        for (MetricKey key : KeyRegistry.getKeys()) {
+            int index = ((AbstractKey) key).getIndex();
+            if (index < metricValues.length && metricValues[index] != 0) {
+                String name = key.getName();
+                long value = metricValues[index];
+                String valueString;
+                if (key instanceof TimerKey) {
+                    // Report timers in ms
+                    TimerKey timer = (TimerKey) key;
+                    long ms = timer.getTimeUnit().toMillis(value);
+                    if (ms == 0) {
+                        continue;
+                    }
+                    valueString = ms + "ms";
+                } else {
+                    valueString = String.valueOf(value);
+                }
+                res.put(name, valueString);
+                maxKeyWidth = Math.max(maxKeyWidth, name.length());
+            }
+        }
+
+        String title = String.format("%s [id:%s compilation:%d compilation_id:%s]", compilableName, identity, compilationNr, compilationId);
+        out.println(new String(new char[title.length()]).replace('\0', '#'));
+        out.printf("%s%n", title);
+        out.println(new String(new char[title.length()]).replace('\0', '~'));
+
+        for (Map.Entry<String, String> e : res.entrySet()) {
+            out.printf("%-" + String.valueOf(maxKeyWidth) + "s = %20s%n", e.getKey(), e.getValue());
+        }
+        out.println();
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugCounter.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-/**
- * A counter for some value of interest.
- */
-public interface DebugCounter {
-
-    /**
-     * Adds 1 to this counter if counting is {@link Debug#isCountEnabled() enabled} or this is an
-     * {@linkplain #isConditional() unconditional} counter.
-     */
-    void increment();
-
-    /**
-     * Adds {@code value} to this counter if counting is {@link Debug#isCountEnabled() enabled} or
-     * this is an {@linkplain #isConditional() unconditional} counter.
-     */
-    void add(long value);
-
-    /**
-     * Sets a flag determining if this counter is only enabled if counting is
-     * {@link Debug#isCountEnabled() enabled}.
-     */
-    void setConditional(boolean flag);
-
-    /**
-     * Determines if this counter is only enabled if counting is {@link Debug#isCountEnabled()
-     * enabled}.
-     */
-    boolean isConditional();
-
-    /**
-     * Gets the current value of this counter.
-     */
-    long getCurrentValue();
-
-    /**
-     * Determines if this counter is enabled (either conditionally or unconditionally).
-     */
-    default boolean isEnabled() {
-        return !isConditional() || Debug.isCountEnabled();
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,23 +24,34 @@
 
 import java.io.Closeable;
 
-public interface DebugDumpHandler extends Closeable {
-
-    void dump(Object object, String format, Object... arguments);
+/**
+ * Interface implemented by classes that provide an external visualization of selected object types
+ * such as compiler graphs and nodes. The format and client required to consume the visualizations
+ * is determined by the implementation. For example, a dumper may convert a compiler node to a human
+ * readable string and print it to the console. A more sophisticated dumper may serialize a compiler
+ * graph and send it over the network to a tool (e.g., https://github.com/graalvm/visualizer) that
+ * can display graphs.
+ */
+public interface DebugDumpHandler extends Closeable, DebugHandler {
 
     /**
-     * Add arbitrary capability for use by the handler.
+     * If the type of {@code object} is supported by this dumper, then a representation of
+     * {@code object} is sent to some consumer in a format determined by this object.
      *
-     * @param capability
+     * @param debug the debug context requesting the dump
+     * @param object the object to be dumped
+     * @param format a format string specifying a title that describes the context of the dump
+     *            (e.g., the compiler phase in which request is made)
+     * @param arguments arguments referenced by the format specifiers in {@code format}
      */
-    default void addCapability(Object capability) {
-    }
+    void dump(DebugContext debug, Object object, String format, Object... arguments);
 
     /**
      * Flushes and releases resources managed by this dump handler. A subsequent call to
-     * {@link #dump(java.lang.Object, java.lang.String, java.lang.Object...)} will create and open
-     * new resources. That is, this method can be used to reset the handler.
+     * {@link #dump} will create and open new resources. That is, this method can be used to reset
+     * the handler.
      */
     @Override
-    void close();
+    default void close() {
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugEnvironment.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Count;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Dump;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Log;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.MethodFilter;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.MethodMeter;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Time;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.TrackMemUse;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Verify;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.serviceprovider.GraalServices;
-
-import jdk.vm.ci.runtime.JVMCI;
-
-public class DebugEnvironment {
-
-    /**
-     * Create a GraalDebugConfig if {@link Debug#isEnabled()} is true and one hasn't already been
-     * created. Additionally add {@code extraArgs} as capabilities to the {@link DebugDumpHandler}s
-     * associated with the current config. Capabilities can be added at any time.
-     *
-     * @return the current {@link GraalDebugConfig} or null if nothing was done
-     */
-    public static GraalDebugConfig ensureInitialized(OptionValues options, Object... capabilities) {
-        return ensureInitializedHelper(options, false, capabilities);
-    }
-
-    /**
-     * Create a new GraalDebugConfig if {@link Debug#isEnabled()} is true, even if one had already
-     * been created. Additionally add {@code extraArgs} as capabilities to the
-     * {@link DebugDumpHandler}s associated with the current config. Capabilities can be added at
-     * any time.
-     *
-     * @return the current {@link GraalDebugConfig} or null if nothing was done
-     */
-    public static GraalDebugConfig forceInitialization(OptionValues options, Object... capabilities) {
-        return ensureInitializedHelper(options, true, capabilities);
-    }
-
-    private static GraalDebugConfig ensureInitializedHelper(OptionValues options, boolean forceInit, Object... capabilities) {
-        if (!Debug.isEnabled()) {
-            return null;
-        }
-        GraalDebugConfig debugConfig = (GraalDebugConfig) DebugScope.getConfig();
-        if (debugConfig == null || forceInit || options != debugConfig.getOptions()) {
-            // Initialize JVMCI before loading class Debug
-            JVMCI.initialize();
-            List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
-            List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
-            debugConfig = new GraalDebugConfig(
-                            options,
-                            Log.getValue(options),
-                            Count.getValue(options),
-                            TrackMemUse.getValue(options),
-                            Time.getValue(options),
-                            Dump.getValue(options),
-                            Verify.getValue(options),
-                            MethodFilter.getValue(options),
-                            MethodMeter.getValue(options),
-                            TTY.out, dumpHandlers, verifyHandlers);
-
-            for (DebugConfigCustomizer customizer : GraalServices.load(DebugConfigCustomizer.class)) {
-                customizer.customize(debugConfig);
-            }
-
-            Debug.setConfig(debugConfig);
-        }
-        if (capabilities != null) {
-            for (Object o : capabilities) {
-                for (DebugDumpHandler handler : debugConfig.dumpHandlers()) {
-                    handler.addCapability(o);
-                }
-            }
-        }
-        return debugConfig;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,44 +25,43 @@
 import java.util.Arrays;
 import java.util.regex.Pattern;
 
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
-import org.graalvm.compiler.debug.internal.DebugScope;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 
 /**
- * Implements the filter specified by the {@link Options#Dump}, {@link Options#Log},
- * {@link Options#Count},{@link Options#MethodMeter} and {@link Options#Time} options.
+ * Implements the filter specified by the {@link DebugOptions#Dump}, {@link DebugOptions#Log},
+ * {@link DebugOptions#Count} and {@link DebugOptions#Time} options.
  * <p>
  * These options enable the associated debug facility if their filter matches the
- * {@linkplain DebugScope#getQualifiedName() name} of the {@linkplain Debug#currentScope() current
- * scope}. For the {@link Options#Dump} and {@link Options#Log} options, the log or dump level is
- * set. The {@link Options#Count},{@link Options#MethodMeter} and {@link Options#Time} options don't
- * have a level, for them {@code level = 0} means disabled and a {@code level > 0} means enabled.
+ * {@linkplain Scope#getQualifiedName() name} of the current scope. For the
+ * {@link DebugOptions#Dump} and {@link DebugOptions#Log} options, the log or dump level is set. The
+ * {@link DebugOptions#Count} and {@link DebugOptions#Time} options don't have a level, for them
+ * {@code level = 0} means disabled and a {@code level > 0} means enabled.
  * <p>
  * 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_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 DebugContext#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_LEVEL}.
+ * level of {@link DebugContext#BASIC_LEVEL}.
  *
  * <h2>Examples of filters</h2>
  *
  * <ul>
  * <li>(empty string)<br>
- * Matches any scope with log level {@link Debug#BASIC_LEVEL}.
+ * Matches any scope with log level {@link DebugContext#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_LEVEL}.
+ * Matches any scope with log level {@link DebugContext#BASIC_LEVEL}.
  *
  * <li>{@code CodeGen,CodeInstall}<br>
  * Matches scopes containing "CodeGen" or "CodeInstall", both with log level
- * {@link Debug#BASIC_LEVEL}.
+ * {@link DebugContext#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 +73,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_LEVEL}.
+ * Matches scopes starting with "Code" with log level {@link DebugContext#BASIC_LEVEL}.
  *
  * <li>{@code Code,~Dead}<br>
- * Matches scopes containing "Code" but not "Dead", with log level {@link Debug#BASIC_LEVEL}.
+ * Matches scopes containing "Code" but not "Dead", with log level {@link DebugContext#BASIC_LEVEL}.
  * </ul>
  */
 final class DebugFilter {
@@ -108,7 +107,7 @@
                         level = 0;
                     } else {
                         pattern = t;
-                        level = Debug.BASIC_LEVEL;
+                        level = DebugContext.BASIC_LEVEL;
                     }
                 } else {
                     pattern = t.substring(0, idx);
@@ -119,13 +118,13 @@
                         } catch (NumberFormatException e) {
                             switch (levelString) {
                                 case "basic":
-                                    level = Debug.BASIC_LEVEL;
+                                    level = DebugContext.BASIC_LEVEL;
                                     break;
                                 case "info":
-                                    level = Debug.INFO_LEVEL;
+                                    level = DebugContext.INFO_LEVEL;
                                     break;
                                 case "verbose":
-                                    level = Debug.VERBOSE_LEVEL;
+                                    level = DebugContext.VERBOSE_LEVEL;
                                     break;
                                 default:
                                     throw new IllegalArgumentException("Unknown dump level: \"" + levelString + "\" expected basic, info, verbose or an integer");
@@ -133,7 +132,7 @@
                         }
 
                     } else {
-                        level = Debug.BASIC_LEVEL;
+                        level = DebugContext.BASIC_LEVEL;
                     }
                 }
 
@@ -147,7 +146,7 @@
      */
     public int matchLevel(String input) {
         if (terms == null) {
-            return Debug.BASIC_LEVEL;
+            return DebugContext.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/DebugHandler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * This interface exists to unify {@link DebugDumpHandler} and {@link DebugVerifyHandler} for the
+ * sake of {@link DebugHandlersFactory#createHandlers}.
+ */
+public interface DebugHandler {
+}
--- /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/DebugHandlersFactory.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.serviceprovider.GraalServices;
+
+/**
+ * Factory for creating {@link DebugHandler}s.
+ */
+public interface DebugHandlersFactory {
+
+    /**
+     * Creates {@link DebugHandler}s based on {@code options}.
+     */
+    List<DebugHandler> createHandlers(OptionValues options);
+
+    /**
+     * Loads {@link DebugHandlersFactory}s on demand via {@link GraalServices#load(Class)}.
+     */
+    Iterable<DebugHandlersFactory> LOADER = new Iterable<DebugHandlersFactory>() {
+        @Override
+        public Iterator<DebugHandlersFactory> iterator() {
+            return GraalServices.load(DebugHandlersFactory.class).iterator();
+        }
+    };
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHistogram.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2013, 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 java.util.List;
-
-/**
- * Facility for recording value frequencies.
- */
-public interface DebugHistogram {
-
-    /**
-     * Gets the name specified when this objected was {@linkplain Debug#createHistogram(String)
-     * created}.
-     */
-    String getName();
-
-    /**
-     * Increments the count for a given value.
-     */
-    void add(Object value);
-
-    void add(Object value, long count);
-
-    /**
-     * A value and a frequency. The ordering imposed by {@link #compareTo(CountedValue)} places
-     * values with higher frequencies first.
-     */
-    class CountedValue implements Comparable<CountedValue> {
-
-        private long count;
-        private final Object value;
-
-        public CountedValue(long count, Object value) {
-            this.count = count;
-            this.value = value;
-        }
-
-        @Override
-        public int compareTo(CountedValue o) {
-            if (count < o.count) {
-                return 1;
-            } else if (count > o.count) {
-                return -1;
-            }
-            return 0;
-        }
-
-        @Override
-        public String toString() {
-            return count + " -> " + value;
-        }
-
-        public void inc() {
-            count++;
-        }
-
-        public void add(long n) {
-            count += n;
-        }
-
-        public long getCount() {
-            return count;
-        }
-
-        public Object getValue() {
-            return value;
-        }
-    }
-
-    /**
-     * Gets a list of the counted values, sorted in descending order of frequency.
-     */
-    List<CountedValue> getValues();
-
-    /**
-     * Interface for a service that can render a visualization of a histogram.
-     */
-    public interface Printer {
-
-        void print(DebugHistogram histogram);
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugInitializationParticipant.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import org.graalvm.compiler.debug.Debug.Params;
-
-/**
- * Defines a service that can modify the {@linkplain Params parameters} for {@link Debug}.
- */
-public interface DebugInitializationParticipant {
-
-    /**
-     * Modifies the given {@link Debug} initialization parameters as necessary.
-     */
-    void apply(Params params);
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugMemUseTracker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugMemUseTracker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -38,7 +38,7 @@
 
     /**
      * Creates a point from which memory usage will be recorded if memory use tracking is
-     * {@linkplain Debug#isMemUseTrackingEnabled() enabled}.
+     * {@linkplain DebugContext#isMemUseTrackingEnabled() enabled}.
      *
      * @return an object that must be closed once the activity has completed to add the memory used
      *         since this call to the total for this tracker
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugMethodMetrics.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-/**
- * A set of debug metrics for all compilations of a {@link ResolvedJavaMethod}. A method metrics
- * object is a container for several metrics per compilation of a method {@code <Method,List
- * <CompilationData>>}. Metrics are stored on a per-method per-compilation basis.
- *
- * <pre>
- * DebugMethodMetrics m = Debug.methodMetrics(method);
- * m.incrementMetric("MyPerCompilationmetric");
- * </pre>
- *
- * In contrast to global metrics like {@link DebugCounter}, {@link DebugTimer} or
- * {@linkplain DebugMemUseTracker}, method compilation metrics are always associated with a
- * {@link ResolvedJavaMethod}.
- */
-public interface DebugMethodMetrics {
-
-    /**
-     * Adds {@code value} to the metric for the current compilation associated with
-     * {@code metricName}. If the metric is yet undefined for the current compilation a new metric
-     * for the given name is defined and the value for it is set to {@code value}.
-     *
-     * @param metricName the name for the metric to be incremented
-     * @param value the value to add to the metric defined by name
-     */
-    void addToMetric(long value, String metricName);
-
-    /**
-     * @see #addToMetric(long, String)
-     */
-    void addToMetric(long value, String format, Object arg1);
-
-    /**
-     * @see #addToMetric(long, String)
-     */
-    void addToMetric(long value, String format, Object arg1, Object arg2);
-
-    /**
-     * @see #addToMetric(long, String)
-     */
-    void addToMetric(long value, String format, Object arg1, Object arg2, Object arg3);
-
-    /**
-     * Adds {@code 1} to the metric for the current compilation associated with {@code metricName}.
-     * If the metric is yet undefined for the current compilation a new metric for the given name is
-     * defined and the value for it is set to {@code value}.
-     *
-     * @param metricName the name for the metric to be incremented
-     */
-    void incrementMetric(String metricName);
-
-    /**
-     * @see #incrementMetric(String)
-     */
-    void incrementMetric(String format, Object arg1);
-
-    /**
-     * @see #incrementMetric(String)
-     */
-    void incrementMetric(String format, Object arg1, Object arg2);
-
-    /**
-     * @see #incrementMetric(String)
-     */
-    void incrementMetric(String format, Object arg1, Object arg2, Object arg3);
-
-    /**
-     * Gets the value of the metric for the current compilation associated with the
-     * {@code metricName} . If the metric is yet undefined for the current compilation {@code 0} is
-     * returned instead.
-     *
-     * @param metricName the name of the metric for which the value will be returned
-     * @return the value of the metric for the given compilation or {@code 0} if it is not defined
-     */
-    long getCurrentMetricValue(String metricName);
-
-    /**
-     * @see #getCurrentMetricValue(String)
-     */
-    long getCurrentMetricValue(String format, Object arg1);
-
-    /**
-     * @see #getCurrentMetricValue(String)
-     */
-    long getCurrentMetricValue(String format, Object arg1, Object arg2);
-
-    /**
-     * @see #getCurrentMetricValue(String)
-     */
-    long getCurrentMetricValue(String format, Object arg1, Object arg2, Object arg3);
-
-    /**
-     * Gets the {@link ResolvedJavaMethod} associated with this {@linkplain DebugMethodMetrics}.
-     *
-     * @return the {@link ResolvedJavaMethod} of the current method metric
-     */
-    ResolvedJavaMethod getMethod();
-
-}
--- /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/DebugOptions.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,208 @@
+/*
+ * 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 java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionType;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
+
+/**
+ * Options that configure a {@link DebugContext} and related functionality.
+ */
+public class DebugOptions {
+    static class DeprecatedOptionKey<T> extends OptionKey<T> {
+        private final OptionKey<T> replacement;
+
+        DeprecatedOptionKey(OptionKey<T> replacement) {
+            super(replacement.getDefaultValue());
+            this.replacement = replacement;
+        }
+
+        @Override
+        protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, T oldValue, T newValue) {
+            // Ideally we'd use TTY here but it may not yet be initialized.
+            System.err.printf("Warning: the %s option is deprecated - use %s instead%n", getName(), replacement.getName());
+            replacement.update(values, newValue);
+        }
+    }
+
+    // @formatter:off
+    @Option(help = "Comma separated names of timers that are enabled irrespective of the value for Time option. " +
+                   "An empty value enables all timers unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> Timers = new OptionKey<>(null);
+    @Option(help = "Comma separated names of counters that are enabled irrespective of the value for Count option. " +
+                   "An empty value enables all counters unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> Counters = new OptionKey<>(null);
+    @Option(help = "Comma separated names of memory usage trackers that are enabled irrespective of the value for TrackMemUse option. " +
+                   "An empty value enables all memory usage trackers unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> MemUseTrackers = new OptionKey<>(null);
+
+    @Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " +
+                   "An empty value enables all counters unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> Count = new OptionKey<>(null);
+    @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " +
+                   "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> TrackMemUse = new OptionKey<>(null);
+    @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " +
+                   "An empty value enables all timers unconditionally.", type = OptionType.Debug)
+    public static final OptionKey<String> Time = new OptionKey<>(null);
+
+    @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug)
+    public static final OptionKey<String> Verify = new OptionKey<>(Assertions.ENABLED ? "" : null);
+    @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug)
+    public static final OptionKey<String> Dump = new OptionKey<>(null);
+    @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug)
+    public static final OptionKey<String> Log = new OptionKey<>(null);
+
+    @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug)
+    public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
+    @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug)
+    public static final OptionKey<Boolean> MethodFilterRootOnly = new OptionKey<>(false);
+    @Option(help = "Dump a before and after graph if the named phase changes the graph.%n" +
+                   "The argument is substring matched against the simple name of the phase class", type = OptionType.Debug)
+    public static final OptionKey<String> DumpOnPhaseChange = new OptionKey<>(null);
+
+    @Option(help = "Listst the console at VM shutdown the metric names available to the Timers, Counters and MemUseTrackers option. " +
+                   "Note that this only lists the metrics that were initialized during the VM execution and so " +
+                   "will not include metrics for compiler code that is not executed.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> ListMetrics = new OptionKey<>(false);
+    @Option(help = "File to which metrics are dumped per compilation. A CSV format is used if the file ends with .csv " +
+                    "otherwise a more human readable format is used. The fields in the CSV format are: " +
+                    "compilable, compilable_identity, compilation_nr, compilation_id, metric_name, metric_value", type = OptionType.Debug)
+     public static final OptionKey<String> MetricsFile = new OptionKey<>(null);
+    @Option(help = "File to which aggregated metrics are dumped at shutdown. A CSV format is used if the file ends with .csv " +
+                    "otherwise a more human readable format is used. If not specified, metrics are dumped to the console.", type = OptionType.Debug)
+    public static final OptionKey<String> AggregatedMetricsFile = new OptionKey<>(null);
+
+    @Option(help = "Only report metrics for threads whose name matches the regular expression.", type = OptionType.Debug)
+    public static final OptionKey<String> MetricsThreadFilter = new OptionKey<>(null);
+    @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);
+    @Option(help = "Enable more verbose log output when available", type = OptionType.Debug)
+    public static final OptionKey<Boolean> LogVerbose = new OptionKey<>(false);
+
+    @Option(help = "The directory where various Graal dump files are written.")
+    public static final OptionKey<String> DumpPath = new OptionKey<>("dumps");
+    @Option(help = "Print the name of each dump file path as it's created.")
+    public static final OptionKey<Boolean> ShowDumpFiles = new OptionKey<>(Assertions.ENABLED);
+
+    @Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintCFG = new OptionKey<>(false);
+    @Option(help = "Enable dumping LIR, register allocation and code generation info to the C1Visualizer.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintBackendCFG = new OptionKey<>(true);
+
+    @Option(help = "Output probabilities for fixed nodes during binary graph dumping.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintGraphProbabilities = new OptionKey<>(false);
+    @Option(help = "Enable dumping to the IdealGraphVisualizer.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintGraph = new OptionKey<>(true);
+    @Option(help = "Dump graphs in binary format instead of XML format.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintBinaryGraphs = new OptionKey<>(true);
+    @Option(help = "Print graphs to files instead of sending them over the network.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintGraphFile = new OptionKey<>(false);
+
+    @Option(help = "Host part of the address to which graphs are dumped.", type = OptionType.Debug)
+    public static final OptionKey<String> PrintGraphHost = new OptionKey<>("127.0.0.1");
+    @Option(help = "Port part of the address to which graphs are dumped in XML format (ignored if PrintBinaryGraphs=true).", type = OptionType.Debug)
+    public static final OptionKey<Integer> PrintXmlGraphPort = new OptionKey<>(4444);
+    @Option(help = "Port part of the address to which graphs are dumped in binary format (ignored if PrintBinaryGraphs=false).", type = OptionType.Debug)
+    public static final OptionKey<Integer> PrintBinaryGraphPort = new OptionKey<>(4445);
+    @Option(help = "Schedule graphs as they are dumped.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintGraphWithSchedule = new OptionKey<>(false);
+
+    @Option(help = "Enable dumping Truffle ASTs to the IdealGraphVisualizer.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintTruffleTrees = new OptionKey<>(true);
+
+    @Option(help = "Treat any exceptions during dumping as fatal.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> DumpingErrorsAreFatal = new OptionKey<>(false);
+
+    @Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> PrintCanonicalGraphStrings = new OptionKey<>(false);
+    @Option(help = "Choose format used when dumping canonical text for graphs: " +
+            "0 gives a scheduled graph (better for spotting changes involving the schedule)" +
+            "while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug)
+    public static final OptionKey<Integer> PrintCanonicalGraphStringFlavor = new OptionKey<>(0);
+    @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> CanonicalGraphStringsExcludeVirtuals = new OptionKey<>(true);
+    @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> CanonicalGraphStringsCheckConstants = new OptionKey<>(false);
+    @Option(help = "Attempts to remove object identity hashes when dumping canonical text for graphs.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> CanonicalGraphStringsRemoveIdentities = new OptionKey<>(true);
+
+    @Option(help = "Clear the debug metrics after bootstrap.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> ClearMetricsAfterBootstrap = new OptionKey<>(false);
+    @Option(help = "Do not compile anything on bootstrap but just initialize the compiler.", type = OptionType.Debug)
+    public static final OptionKey<Boolean> BootstrapInitializeOnly = new OptionKey<>(false);
+
+    // These will be removed at some point
+    @Option(help = "Deprecated - use PrintGraphHost instead.", type = OptionType.Debug)
+    static final OptionKey<String> PrintIdealGraphAddress = new DebugOptions.DeprecatedOptionKey<>(PrintGraphHost);
+    @Option(help = "Deprecated - use PrintGraphWithSchedule instead.", type = OptionType.Debug)
+    static final OptionKey<Boolean> PrintIdealGraphSchedule = new DebugOptions.DeprecatedOptionKey<>(PrintGraphWithSchedule);
+    @Option(help = "Deprecated - use PrintGraph instead.", type = OptionType.Debug)
+    static final OptionKey<Boolean> PrintIdealGraph = new DebugOptions.DeprecatedOptionKey<>(PrintGraph);
+    @Option(help = "Deprecated - use PrintGraphFile instead.", type = OptionType.Debug)
+    static final OptionKey<Boolean> PrintIdealGraphFile = new DebugOptions.DeprecatedOptionKey<>(PrintGraphFile);
+    @Option(help = "Deprecated - use PrintXmlGraphPort instead.", type = OptionType.Debug)
+    static final OptionKey<Integer> PrintIdealGraphPort = new DebugOptions.DeprecatedOptionKey<>(PrintXmlGraphPort);
+    // @formatter:on
+
+    /**
+     * Gets the directory in which {@link DebugDumpHandler}s can generate output. This will be the
+     * directory specified by {@link #DumpPath} if it has been set otherwise it will be derived from
+     * the default value of {@link #DumpPath} and {@link UniquePathUtilities#getGlobalTimeStamp()}.
+     *
+     * This method will ensure the returned directory exists, printing a message to {@link TTY} if
+     * it creates it.
+     *
+     * @return a path as described above whose directories are guaranteed to exist
+     * @throws IOException if there was an error in {@link Files#createDirectories}
+     */
+    public static Path getDumpDirectory(OptionValues options) throws IOException {
+        Path dumpDir;
+        if (DumpPath.hasBeenSet(options)) {
+            dumpDir = Paths.get(DumpPath.getValue(options));
+        } else {
+            dumpDir = Paths.get(DumpPath.getValue(options), String.valueOf(UniquePathUtilities.getGlobalTimeStamp()));
+        }
+        if (!Files.exists(dumpDir)) {
+            synchronized (DebugConfigImpl.class) {
+                if (!Files.exists(dumpDir)) {
+                    Files.createDirectories(dumpDir);
+                    TTY.println("Dumping debug output in %s", dumpDir.toAbsolutePath().toString());
+                }
+            }
+        }
+        return dumpDir;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugRetryableTask.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugRetryableTask.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,28 +22,26 @@
  */
 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 {
+public abstract class DebugRetryableTask<T> {
 
     /**
      * 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}.
+     * {@link #getRetryContext} and if that returns a non-null value,
+     * {@link #run(DebugContext, Throwable)} is called with it.
+     *
+     * @param initialDebug the debug context to be used for the initial execution
      */
     @SuppressWarnings("try")
-    public final T execute() {
+    public final T runWithRetry(DebugContext initialDebug) {
         try {
-            return run(null);
+            return run(initialDebug, 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);
-                }
+            DebugContext retryDebug = getRetryContext(initialDebug, t);
+            if (retryDebug != null) {
+                return run(retryDebug, t);
             } else {
                 throw t;
             }
@@ -51,22 +49,24 @@
     }
 
     /**
-     * Runs this task.
+     * Runs this body of 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)}
+     * @param debug the debug context to use for the execution
+     * @param failure {@code null} if this is the first execution otherwise the cause of the first
+     *            execution to fail
      */
-    protected abstract T run(Throwable failure);
+    protected abstract T run(DebugContext debug, 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.
+     * Notifies this object that the initial execution failed with exception {@code t} and requests
+     * a debug context to be used for re-execution.
      *
+     * @param initialDebug the debug context used for the initial execution
      * @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.
+     * @return the debug context to be used for re-executing this task or {@code null} if {@code t}
+     *         should immediately be re-thrown without re-executing this task
      */
-    protected boolean onRetry(Throwable t) {
-        return true;
+    protected DebugContext getRetryContext(DebugContext initialDebug, Throwable t) {
+        return null;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugTimer.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A timer for some activity of interest. A timer should be deployed using the try-with-resources
- * pattern:
- *
- * <pre>
- * try (TimerCloseable a = timer.start()) {
- *     // the code to time
- * }
- * </pre>
- */
-public interface DebugTimer {
-
-    /**
-     * Starts this timer if timing is {@linkplain Debug#isTimeEnabled() enabled} or this is an
-     * {@linkplain #isConditional() unconditional} timer.
-     *
-     * @return an object that must be closed once the activity has completed to add the elapsed time
-     *         since this call to the total for this timer
-     */
-    DebugCloseable start();
-
-    /**
-     * Sets a flag determining if this timer is only enabled if timing is
-     * {@link Debug#isTimeEnabled() enabled}.
-     */
-    void setConditional(boolean flag);
-
-    /**
-     * Determines if this timer is only enabled if timing is {@link Debug#isTimeEnabled() enabled}.
-     */
-    boolean isConditional();
-
-    /**
-     * Gets the current value of this timer.
-     */
-    long getCurrentValue();
-
-    /**
-     * Gets the time unit of this timer.
-     */
-    TimeUnit getTimeUnit();
-
-    /**
-     * Gets the timer recording the amount time spent within a timed scope minus the time spent in
-     * any nested timed scopes.
-     *
-     * @return null if this timer does not support flat timing
-     */
-    default DebugTimer getFlat() {
-        return null;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugValueFactory.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-/**
- * A factory for creating {@link DebugCounter}s, {@link DebugTimer}s, {@link DebugMemUseTracker}s
- * and {@link DebugMethodMetrics}.
- */
-public interface DebugValueFactory {
-
-    DebugCounter createCounter(String name, boolean conditional);
-
-    DebugTimer createTimer(String name, boolean conditional);
-
-    DebugMemUseTracker createMemUseTracker(String name, boolean conditional);
-
-    DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method);
-}
--- /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/DebugValueMap.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A node in a tree of values.
+ */
+public class DebugValueMap {
+
+    /**
+     * The top level maps for all threads.
+     */
+    private static final List<DebugValueMap> topLevelMaps = new ArrayList<>();
+
+    private long[] values;
+    private List<DebugValueMap> children;
+    private String name;
+
+    public DebugValueMap(String name) {
+        this.name = name;
+    }
+
+    public void setCurrentValue(int index, long l) {
+        ensureSize(index);
+        values[index] = l;
+    }
+
+    public long getCurrentValue(int index) {
+        ensureSize(index);
+        return values[index];
+    }
+
+    public void clearChildren() {
+        if (children != null) {
+            children.clear();
+        }
+    }
+
+    public void reset() {
+        if (values != null) {
+            Arrays.fill(values, 0L);
+        }
+        if (children != null) {
+            for (DebugValueMap child : children) {
+                child.reset();
+            }
+        }
+    }
+
+    private void ensureSize(int index) {
+        if (values == null) {
+            values = new long[index + 1];
+        }
+        if (values.length <= index) {
+            values = Arrays.copyOf(values, index + 1);
+        }
+    }
+
+    private int capacity() {
+        return (values == null) ? 0 : values.length;
+    }
+
+    public void addChild(DebugValueMap map) {
+        if (children == null) {
+            children = new ArrayList<>(4);
+        }
+        children.add(map);
+    }
+
+    public List<DebugValueMap> getChildren() {
+        if (children == null) {
+            return Collections.emptyList();
+        } else {
+            return Collections.unmodifiableList(children);
+        }
+    }
+
+    public boolean hasChildren() {
+        return children != null && !children.isEmpty();
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public String toString() {
+        return "DebugValueMap<" + getName() + ">";
+    }
+
+    public static synchronized void registerTopLevel(DebugValueMap map) {
+        topLevelMaps.add(map);
+    }
+
+    public static synchronized List<DebugValueMap> getTopLevelMaps() {
+        return topLevelMaps;
+    }
+
+    /**
+     * The top level map for the current thread.
+     */
+    private static final ThreadLocal<DebugValueMap> topLevelMap = new ThreadLocal<>();
+
+    static DebugValueMap getTopLevelMap() {
+        DebugValueMap map = topLevelMap.get();
+        if (map == null) {
+            map = new DebugValueMap(Thread.currentThread().getName());
+            topLevelMap.set(map);
+            registerTopLevel(map);
+        }
+        return map;
+    }
+
+    public void normalize() {
+        if (hasChildren()) {
+            Map<String, DebugValueMap> occurred = new HashMap<>();
+            for (DebugValueMap map : children) {
+                String mapName = map.getName();
+                if (!occurred.containsKey(mapName)) {
+                    occurred.put(mapName, map);
+                    map.normalize();
+                } else {
+                    occurred.get(mapName).mergeWith(map);
+                    occurred.get(mapName).normalize();
+                }
+            }
+
+            if (occurred.values().size() < children.size()) {
+                // At least one duplicate was found.
+                children.clear();
+                for (DebugValueMap map : occurred.values()) {
+                    addChild(map);
+                    map.normalize();
+                }
+            }
+        }
+    }
+
+    private void mergeWith(DebugValueMap map) {
+        if (map.hasChildren()) {
+            if (hasChildren()) {
+                children.addAll(map.children);
+            } else {
+                children = map.children;
+            }
+            map.children = null;
+        }
+
+        int size = Math.max(this.capacity(), map.capacity());
+        ensureSize(size);
+        for (int i = 0; i < size; ++i) {
+            long curValue = getCurrentValue(i);
+            long otherValue = map.getCurrentValue(i);
+            setCurrentValue(i, curValue + otherValue);
+        }
+    }
+
+    public void group() {
+        if (this.hasChildren()) {
+            List<DebugValueMap> oldChildren = new ArrayList<>(this.children);
+            this.children.clear();
+            for (DebugValueMap map : oldChildren) {
+                mergeWith(map);
+            }
+        }
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,14 +25,16 @@
 /**
  * Performs some kind of verification on an object.
  */
-public interface DebugVerifyHandler {
+public interface DebugVerifyHandler extends DebugHandler {
 
     /**
      * Verifies that a given object satisfies some invariants.
      *
      * @param object object to verify
-     * @param format description of verification context
-     * @param args arguments for the format
+     * @param debug the debug context requesting the dump
+     * @param format a format string specifying a title that describes the context of the
+     *            verification (e.g., the compiler phase in which request is made)
+     * @param arguments arguments referenced by the format specifiers in {@code format}
      */
-    void verify(Object object, String format, Object... args);
+    void verify(DebugContext debug, Object object, String format, Object... arguments);
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DelegatingDebugConfig.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-/*
- * Copyright (c) 2013, 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 java.io.PrintStream;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.Map;
-
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.options.OptionValues;
-
-public class DelegatingDebugConfig implements DebugConfig {
-
-    protected final DebugConfig delegate;
-
-    /**
-     * The features of a {@link DelegatingDebugConfig} that can be force
-     * {@linkplain DelegatingDebugConfig#enable(Feature) enabled}/
-     * {@linkplain DelegatingDebugConfig#disable(Feature) disabled} or
-     * {@linkplain DelegatingDebugConfig#delegate(Feature) delegated}.
-     */
-    public enum Feature {
-        /**
-         * @see Debug#isLogEnabledForMethod()
-         */
-        LOG_METHOD,
-        /**
-         * @see Debug#isDumpEnabledForMethod()
-         */
-        DUMP_METHOD,
-        /**
-         * @see Debug#isVerifyEnabled()
-         */
-        VERIFY,
-        /**
-         * @see Debug#isVerifyEnabledForMethod()
-         */
-        VERIFY_METHOD,
-        /**
-         * @see Debug#isCountEnabled()
-         */
-        COUNT,
-        /**
-         * @see Debug#isMethodMeterEnabled()
-         */
-        METHOD_METRICS,
-        /**
-         * @see Debug#isMemUseTrackingEnabled()
-         */
-        TRACK_MEM_USE,
-        /**
-         * @see Debug#isTimeEnabled()
-         */
-        TIME,
-        /**
-         * @see DebugConfig#interceptException(Throwable)
-         */
-        INTERCEPT
-    }
-
-    private final Map<Feature, Boolean> featureState = new EnumMap<>(Feature.class);
-
-    /**
-     * The debug levels of a {@link DelegatingDebugConfig} than can be
-     * {@linkplain DelegatingDebugConfig#override(Level, int) overridden} or
-     * {@linkplain DelegatingDebugConfig#delegate(Level) delegated}.
-     */
-    public enum Level {
-        LOG,
-        DUMP
-    }
-
-    private final Map<Level, Integer> levelState = new EnumMap<>(Level.class);
-
-    /**
-     * Creates a config that delegates to the {@link DebugScope#getConfig() current config}.
-     */
-    public DelegatingDebugConfig() {
-        this(DebugScope.getConfig());
-    }
-
-    /**
-     * Creates a config that delegates to a given config.
-     */
-    public DelegatingDebugConfig(DebugConfig delegate) {
-        this.delegate = delegate;
-    }
-
-    public DelegatingDebugConfig enable(Feature feature) {
-        featureState.put(feature, Boolean.TRUE);
-        return this;
-    }
-
-    public DelegatingDebugConfig disable(Feature feature) {
-        featureState.put(feature, Boolean.FALSE);
-        return this;
-    }
-
-    public DelegatingDebugConfig override(Level level, int newLevel) {
-        levelState.put(level, newLevel);
-        return this;
-    }
-
-    public DelegatingDebugConfig delegate(Feature feature) {
-        featureState.put(feature, null);
-        return this;
-    }
-
-    public DelegatingDebugConfig delegate(Level level) {
-        levelState.put(level, null);
-        return this;
-    }
-
-    @Override
-    public OptionValues getOptions() {
-        return delegate.getOptions();
-    }
-
-    @Override
-    public int getLogLevel() {
-        Integer ls = levelState.get(Level.LOG);
-        if (ls == null) {
-            return delegate.getLogLevel();
-        }
-        return ls.intValue();
-    }
-
-    @Override
-    public boolean isLogEnabledForMethod() {
-        Boolean fs = featureState.get(Feature.LOG_METHOD);
-        if (fs == null) {
-            return delegate.isLogEnabledForMethod();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isCountEnabled() {
-        Boolean fs = featureState.get(Feature.COUNT);
-        if (fs == null) {
-            return delegate.isCountEnabled();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isMemUseTrackingEnabled() {
-        Boolean fs = featureState.get(Feature.TRACK_MEM_USE);
-        if (fs == null) {
-            return delegate.isMemUseTrackingEnabled();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public int getDumpLevel() {
-        Integer ls = levelState.get(Level.DUMP);
-        if (ls == null) {
-            return delegate.getDumpLevel();
-        }
-        return ls.intValue();
-    }
-
-    @Override
-    public boolean isDumpEnabledForMethod() {
-        Boolean fs = featureState.get(Feature.DUMP_METHOD);
-        if (fs == null) {
-            return delegate.isDumpEnabledForMethod();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isVerifyEnabled() {
-        Boolean fs = featureState.get(Feature.VERIFY);
-        if (fs == null) {
-            return delegate.isVerifyEnabled();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isVerifyEnabledForMethod() {
-        Boolean fs = featureState.get(Feature.VERIFY_METHOD);
-        if (fs == null) {
-            return delegate.isVerifyEnabledForMethod();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isTimeEnabled() {
-        Boolean fs = featureState.get(Feature.TIME);
-        if (fs == null) {
-            return delegate.isTimeEnabled();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public boolean isMethodMeterEnabled() {
-        Boolean fs = featureState.get(Feature.METHOD_METRICS);
-        if (fs == null) {
-            return delegate.isMethodMeterEnabled();
-        }
-        return fs.booleanValue();
-    }
-
-    @Override
-    public RuntimeException interceptException(Throwable e) {
-        Boolean fs = featureState.get(Feature.INTERCEPT);
-        if (fs == null || fs) {
-            return delegate.interceptException(e);
-        }
-        return null;
-    }
-
-    @Override
-    public Collection<DebugDumpHandler> dumpHandlers() {
-        return delegate.dumpHandlers();
-    }
-
-    @Override
-    public Collection<DebugVerifyHandler> verifyHandlers() {
-        return delegate.verifyHandlers();
-    }
-
-    @Override
-    public PrintStream output() {
-        return delegate.output();
-    }
-
-    @Override
-    public void addToContext(Object o) {
-        delegate.addToContext(o);
-    }
-
-    @Override
-    public void removeFromContext(Object o) {
-        delegate.removeFromContext(o);
-    }
-
-}
--- /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/GlobalMetrics.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,133 @@
+/*
+ * 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 java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.MapCursor;
+import org.graalvm.util.Pair;
+
+/**
+ * Metric values that can be {@linkplain #add(DebugContext) updated} by multiple threads.
+ */
+public class GlobalMetrics {
+    long[] values;
+
+    /**
+     * Adds the values in {@code debug} to the values in this object.
+     */
+    public synchronized void add(DebugContext debug) {
+        values = debug.addValuesTo(values);
+    }
+
+    /**
+     * Clears all values in this object.
+     */
+    public void clear() {
+        values = null;
+    }
+
+    /**
+     * Creates and returns a sorted map from metric names to their values in this object.
+     */
+    public EconomicMap<MetricKey, Long> asKeyValueMap() {
+        List<MetricKey> keys = KeyRegistry.getKeys();
+        Collections.sort(keys, MetricKey.NAME_COMPARATOR);
+        EconomicMap<MetricKey, Long> res = EconomicMap.create(keys.size());
+        long[] vals = values;
+        for (MetricKey key : keys) {
+            int index = ((AbstractKey) key).getIndex();
+            if (vals == null || index >= vals.length) {
+                res.put(key, 0L);
+            } else {
+                res.put(key, vals[index]);
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Prints the values in the object to the file specified by
+     * {@link DebugOptions#AggregatedMetricsFile} if present otherwise to
+     * {@link DebugContext#DEFAULT_LOG_STREAM}.
+     */
+    public void print(OptionValues options) {
+        long[] vals = values;
+        if (vals != null) {
+            EconomicMap<MetricKey, Long> map = asKeyValueMap();
+            String metricsFile = DebugOptions.AggregatedMetricsFile.getValue(options);
+            boolean csv = metricsFile != null && (metricsFile.endsWith(".csv") || metricsFile.endsWith(".CSV"));
+            try (PrintStream p = metricsFile == null ? DebugContext.DEFAULT_LOG_STREAM : new PrintStream(Files.newOutputStream(Paths.get(metricsFile)))) {
+                if (!csv) {
+                    if (!map.isEmpty()) {
+                        p.println("++ Aggregated Metrics ++");
+                    }
+                }
+                String csvFormat = CSVUtil.buildFormatString("%s", "%s", "%s");
+                MapCursor<MetricKey, Long> e = map.getEntries();
+                while (e.advance()) {
+                    MetricKey key = e.getKey();
+                    if (csv) {
+                        Pair<String, String> valueAndUnit = key.toCSVFormat(e.getValue());
+                        CSVUtil.Escape.println(p, csvFormat, key.getName(), valueAndUnit.getLeft(), valueAndUnit.getRight());
+                    } else {
+                        p.println(key.getName() + "=" + key.toHumanReadableFormat(e.getValue()));
+                    }
+                }
+                if (!csv) {
+                    if (!map.isEmpty()) {
+                        p.println("-- Aggregated Metrics --");
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (DebugOptions.ListMetrics.getValue(options)) {
+            PrintStream p = System.out;
+            p.println("++ Metric Keys ++");
+            List<MetricKey> keys = KeyRegistry.getKeys();
+            Collections.sort(keys, MetricKey.NAME_COMPARATOR);
+            for (MetricKey key : keys) {
+                String name = key.getDocName();
+                if (name != null) {
+                    String doc = key.getDoc();
+                    if (doc != null) {
+                        p.println(name + ": " + doc);
+                    } else {
+                        p.println(name);
+                    }
+                }
+            }
+            p.println("-- Metric Keys --");
+        }
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GraalDebugConfig.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,452 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug;
-
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.graalvm.compiler.options.Option;
-import org.graalvm.compiler.options.OptionKey;
-import org.graalvm.compiler.options.OptionType;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.util.EconomicMap;
-
-import jdk.vm.ci.code.BailoutException;
-import jdk.vm.ci.meta.JavaMethod;
-
-public class GraalDebugConfig implements DebugConfig {
-
-    public static class Options {
-        // @formatter:off
-        @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug)
-        public static final OptionKey<String> Dump = new OptionKey<>(null);
-        @Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " +
-                       "An empty value enables all counters unconditionally.", type = OptionType.Debug)
-        public static final OptionKey<String> Count = new OptionKey<>(null);
-        @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug)
-        public static final OptionKey<String> Verify = new OptionKey<>(Assertions.ENABLED ? "" : null);
-        @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " +
-                       "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug)
-        public static final OptionKey<String> TrackMemUse = new OptionKey<>(null);
-        @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " +
-                       "An empty value enables all timers unconditionally.", type = OptionType.Debug)
-        public static final OptionKey<String> Time = new OptionKey<>(null);
-        @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug)
-        public static final OptionKey<String> Log = new OptionKey<>(null);
-        @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug)
-        public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
-        @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug)
-        public static final OptionKey<Boolean> MethodFilterRootOnly = new OptionKey<>(false);
-        @Option(help = "Dump a before and after graph if the named phase changes the graph.%n" +
-                       "The argument is substring matched against the simple name of the phase class", type = OptionType.Debug)
-        public static final OptionKey<String> DumpOnPhaseChange = new OptionKey<>(null);
-
-        @Option(help = "How to print counters and timing values:%n" +
-                       "Name - aggregate by unqualified name%n" +
-                       "Partial - aggregate by partially qualified name (e.g., A.B.C.D.Counter and X.Y.Z.D.Counter will be merged to D.Counter)%n" +
-                       "Complete - aggregate by qualified name%n" +
-                       "Thread - aggregate by qualified name and thread", type = OptionType.Debug)
-        public static final OptionKey<String> DebugValueSummary = new OptionKey<>("Name");
-        @Option(help = "Print counters and timers in a human readable form.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> DebugValueHumanReadable = new OptionKey<>(true);
-        @Option(help = "Omit reporting 0-value counters", type = OptionType.Debug)
-        public static final OptionKey<Boolean> SuppressZeroDebugValues = new OptionKey<>(true);
-        @Option(help = "Only report debug values for maps which match the regular expression.", type = OptionType.Debug)
-        public static final OptionKey<String> DebugValueThreadFilter = new OptionKey<>(null);
-        @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 = "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);
-        @Option(help = "Enable more verbose log output when available", type = OptionType.Debug)
-        public static final OptionKey<Boolean> LogVerbose = new OptionKey<>(false);
-
-        @Option(help = "The directory where various Graal dump files are written.")
-        public static final OptionKey<String> DumpPath = new OptionKey<>(".");
-
-        @Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintCFG = new OptionKey<>(false);
-        @Option(help = "Enable dumping LIR, register allocation and code generation info to the C1Visualizer.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintBackendCFG = new OptionKey<>(true);
-        @Option(help = "Base filename when dumping C1Visualizer output to files.", type = OptionType.Debug)
-        public static final OptionKey<String> PrintCFGFileName = new OptionKey<>("compilations");
-
-        @Option(help = "Output probabilities for fixed nodes during binary graph dumping.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintGraphProbabilities = new OptionKey<>(false);
-        @Option(help = "Enable dumping to the IdealGraphVisualizer.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintGraph = new OptionKey<>(true);
-        @Option(help = "Dump graphs in binary format instead of XML format.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintBinaryGraphs = new OptionKey<>(true);
-        @Option(help = "Print graphs to files instead of sending them over the network.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintGraphFile = new OptionKey<>(false);
-        @Option(help = "Base filename when dumping graphs to files.", type = OptionType.Debug)
-        public static final OptionKey<String> PrintGraphFileName = new OptionKey<>("runtime-graphs");
-
-        @Option(help = "Host part of the address to which graphs are dumped.", type = OptionType.Debug)
-        public static final OptionKey<String> PrintGraphHost = new OptionKey<>("127.0.0.1");
-        @Option(help = "Port part of the address to which graphs are dumped in XML format (ignored if PrintBinaryGraphs=true).", type = OptionType.Debug)
-        public static final OptionKey<Integer> PrintXmlGraphPort = new OptionKey<>(4444);
-        @Option(help = "Port part of the address to which graphs are dumped in binary format (ignored if PrintBinaryGraphs=false).", type = OptionType.Debug)
-        public static final OptionKey<Integer> PrintBinaryGraphPort = new OptionKey<>(4445);
-        @Option(help = "Schedule graphs as they are dumped.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintGraphWithSchedule = new OptionKey<>(false);
-
-        @Option(help = "Enable dumping Truffle ASTs to the IdealGraphVisualizer.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintTruffleTrees = new OptionKey<>(true);
-
-        @Option(help = "Treat any exceptions during dumping as fatal.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> DumpingErrorsAreFatal = new OptionKey<>(false);
-
-        @Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> PrintCanonicalGraphStrings = new OptionKey<>(false);
-        @Option(help = "Base directory when dumping graphs strings to files.", type = OptionType.Debug)
-        public static final OptionKey<String> PrintCanonicalGraphStringsDirectory = new OptionKey<>("graph-strings");
-        @Option(help = "Choose format used when dumping canonical text for graphs: " +
-                "0 gives a scheduled graph (better for spotting changes involving the schedule)" +
-                "while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug)
-        public static final OptionKey<Integer> PrintCanonicalGraphStringFlavor = new OptionKey<>(0);
-        @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> CanonicalGraphStringsExcludeVirtuals = new OptionKey<>(true);
-        @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> CanonicalGraphStringsCheckConstants = new OptionKey<>(false);
-        @Option(help = "Attempts to remove object identity hashes when dumping canonical text for graphs.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> CanonicalGraphStringsRemoveIdentities = new OptionKey<>(true);
-
-        @Option(help = "Enable per method metrics that are collected across all compilations of a method." +
-                       "Pattern for scope(s) in which method metering is enabled (see DebugFilter and Debug.metric).", type = OptionType.Debug)
-        public static final OptionKey<String> MethodMeter = new OptionKey<>(null);
-        @Option(help = "If a global metric (DebugTimer, DebugCounter or DebugMemUseTracker) is enabled in the same scope as a method metric, " +
-                       "use the global metric to update the method metric for the current compilation. " +
-                       "This option enables the re-use of global metrics on per-compilation basis. " +
-                       "Whenever a value is added to a global metric, the value is also added to a MethodMetric under the same name " +
-                       "as the global metric. " +
-                       "This option incurs a small but constant overhead due to the context method lookup at each metric update. " +
-                       "Format to specify GlobalMetric interception:(Timers|Counters|MemUseTrackers)(,Timers|,Counters|,MemUseTrackers)*", type = OptionType.Debug)
-        public static final OptionKey<String> GlobalMetricsInterceptedByMethodMetrics = new OptionKey<>(null);
-        @Option(help = "Force-enable debug code paths", type = OptionType.Debug)
-        public static final OptionKey<Boolean> ForceDebugEnable = new OptionKey<>(false);
-        @Option(help = "Clear the debug metrics after bootstrap.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> ClearMetricsAfterBootstrap = new OptionKey<>(false);
-        @Option(help = "Do not compile anything on bootstrap but just initialize the compiler.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> BootstrapInitializeOnly = new OptionKey<>(false);
-
-        // These
-        @Option(help = "Deprecated - use PrintGraphHost instead.", type = OptionType.Debug)
-        static final OptionKey<String> PrintIdealGraphAddress = new DeprecatedOptionKey<>(PrintGraphHost);
-        @Option(help = "Deprecated - use PrintGraphWithSchedule instead.", type = OptionType.Debug)
-        static final OptionKey<Boolean> PrintIdealGraphSchedule = new DeprecatedOptionKey<>(PrintGraphWithSchedule);
-        @Option(help = "Deprecated - use PrintGraph instead.", type = OptionType.Debug)
-        static final OptionKey<Boolean> PrintIdealGraph = new DeprecatedOptionKey<>(PrintGraph);
-        @Option(help = "Deprecated - use PrintGraphFile instead.", type = OptionType.Debug)
-        static final OptionKey<Boolean> PrintIdealGraphFile = new DeprecatedOptionKey<>(PrintGraphFile);
-        @Option(help = "Deprecated - use PrintGraphFileName instead.", type = OptionType.Debug)
-        static final OptionKey<String> PrintIdealGraphFileName = new DeprecatedOptionKey<>(PrintGraphFileName);
-        @Option(help = "Deprecated - use PrintXmlGraphPort instead.", type = OptionType.Debug)
-        static final OptionKey<Integer> PrintIdealGraphPort = new DeprecatedOptionKey<>(PrintXmlGraphPort);
-        // @formatter:on
-    }
-
-    static class DeprecatedOptionKey<T> extends OptionKey<T> {
-        private final OptionKey<T> replacement;
-
-        DeprecatedOptionKey(OptionKey<T> replacement) {
-            super(replacement.getDefaultValue());
-            this.replacement = replacement;
-        }
-
-        @Override
-        protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, T oldValue, T newValue) {
-            // Ideally we'd use TTY here but it may not yet be initialized.
-            System.err.printf("Warning: the %s option is deprecated - use %s instead%n", getName(), replacement.getName());
-            replacement.update(values, newValue);
-        }
-    }
-
-    public static boolean isNotEmpty(OptionKey<String> option, OptionValues options) {
-        return option.getValue(options) != null && !option.getValue(options).isEmpty();
-    }
-
-    public static boolean areDebugScopePatternsEnabled(OptionValues options) {
-        return Options.DumpOnError.getValue(options) || Options.Dump.getValue(options) != null || Options.Log.getValue(options) != null || areScopedGlobalMetricsEnabled(options);
-    }
-
-    public static boolean isGlobalMetricsInterceptedByMethodMetricsEnabled(OptionValues options) {
-        return isNotEmpty(Options.GlobalMetricsInterceptedByMethodMetrics, options);
-    }
-
-    /**
-     * Determines if any of {@link Options#Count}, {@link Options#Time} or
-     * {@link Options#TrackMemUse} has a non-null, non-empty value.
-     *
-     * @param options
-     */
-    public static boolean areScopedGlobalMetricsEnabled(OptionValues options) {
-        return isNotEmpty(Options.Count, options) || isNotEmpty(Options.Time, options) || isNotEmpty(Options.TrackMemUse, options) || isNotEmpty(Options.MethodMeter, options);
-    }
-
-    private final OptionValues options;
-
-    private final DebugFilter countFilter;
-    private final DebugFilter logFilter;
-    private final DebugFilter methodMetricsFilter;
-    private final DebugFilter trackMemUseFilter;
-    private final DebugFilter timerFilter;
-    private final DebugFilter dumpFilter;
-    private final DebugFilter verifyFilter;
-    private final MethodFilter[] methodFilter;
-    private final List<DebugDumpHandler> dumpHandlers;
-    private final List<DebugVerifyHandler> verifyHandlers;
-    private final PrintStream output;
-
-    // Use an identity set to handle context objects that don't support hashCode().
-    private final Set<Object> extraFilters = Collections.newSetFromMap(new IdentityHashMap<>());
-
-    public GraalDebugConfig(OptionValues options, String logFilter, String countFilter, String trackMemUseFilter, String timerFilter, String dumpFilter, String verifyFilter, String methodFilter,
-                    String methodMetricsFilter, PrintStream output, List<DebugDumpHandler> dumpHandlers, List<DebugVerifyHandler> verifyHandlers) {
-        this.options = options;
-        this.logFilter = DebugFilter.parse(logFilter);
-        this.countFilter = DebugFilter.parse(countFilter);
-        this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter);
-        this.timerFilter = DebugFilter.parse(timerFilter);
-        this.dumpFilter = DebugFilter.parse(dumpFilter);
-        this.verifyFilter = DebugFilter.parse(verifyFilter);
-        this.methodMetricsFilter = DebugFilter.parse(methodMetricsFilter);
-        if (methodFilter == null || methodFilter.isEmpty()) {
-            this.methodFilter = null;
-        } else {
-            this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter);
-        }
-
-        this.dumpHandlers = dumpHandlers;
-        this.verifyHandlers = verifyHandlers;
-        this.output = output;
-    }
-
-    @Override
-    public OptionValues getOptions() {
-        return options;
-    }
-
-    @Override
-    public int getLogLevel() {
-        return getLevel(logFilter);
-    }
-
-    @Override
-    public boolean isLogEnabledForMethod() {
-        return isEnabledForMethod(logFilter);
-    }
-
-    @Override
-    public boolean isCountEnabled() {
-        return isEnabled(countFilter);
-    }
-
-    @Override
-    public boolean isMemUseTrackingEnabled() {
-        return isEnabled(trackMemUseFilter);
-    }
-
-    @Override
-    public int getDumpLevel() {
-        return getLevel(dumpFilter);
-    }
-
-    @Override
-    public boolean isDumpEnabledForMethod() {
-        return isEnabledForMethod(dumpFilter);
-    }
-
-    @Override
-    public boolean isVerifyEnabled() {
-        return isEnabled(verifyFilter);
-    }
-
-    @Override
-    public boolean isVerifyEnabledForMethod() {
-        return isEnabledForMethod(verifyFilter);
-    }
-
-    @Override
-    public boolean isMethodMeterEnabled() {
-        return isEnabled(methodMetricsFilter);
-    }
-
-    @Override
-    public boolean isTimeEnabled() {
-        return isEnabled(timerFilter);
-    }
-
-    @Override
-    public PrintStream output() {
-        return output;
-    }
-
-    private boolean isEnabled(DebugFilter filter) {
-        return getLevel(filter) > 0;
-    }
-
-    private int getLevel(DebugFilter filter) {
-        int level;
-        if (filter == null) {
-            level = 0;
-        } else {
-            level = filter.matchLevel(Debug.currentScope());
-        }
-        if (level >= 0 && !checkMethodFilter()) {
-            level = -1;
-        }
-        return level;
-    }
-
-    private boolean isEnabledForMethod(DebugFilter filter) {
-        return filter != null && checkMethodFilter();
-    }
-
-    /**
-     * Extracts a {@link JavaMethod} from an opaque debug context.
-     *
-     * @return the {@link JavaMethod} represented by {@code context} or null
-     */
-    public static JavaMethod asJavaMethod(Object context) {
-        if (context instanceof JavaMethodContext) {
-            return ((JavaMethodContext) context).asJavaMethod();
-        }
-        if (context instanceof JavaMethod) {
-            return (JavaMethod) context;
-        }
-        return null;
-    }
-
-    private boolean checkMethodFilter() {
-        if (methodFilter == null && extraFilters.isEmpty()) {
-            return true;
-        } else {
-            JavaMethod lastMethod = null;
-            for (Object o : Debug.context()) {
-                if (extraFilters.contains(o)) {
-                    return true;
-                } else if (methodFilter != null) {
-                    JavaMethod method = asJavaMethod(o);
-                    if (method != null) {
-                        if (!Options.MethodFilterRootOnly.getValue(options)) {
-                            if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) {
-                                return true;
-                            }
-                        } else {
-                            /*
-                             * The context values operate as a stack so if we want MethodFilter to
-                             * only apply to the root method we have to check only the last method
-                             * seen.
-                             */
-                            lastMethod = method;
-                        }
-                    }
-                }
-            }
-            if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) {
-                return true;
-            }
-            return false;
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Debug config:");
-        add(sb, "Log", logFilter);
-        add(sb, "Count", countFilter);
-        add(sb, "MethodMeter", methodMetricsFilter);
-        add(sb, "Time", timerFilter);
-        add(sb, "Dump", dumpFilter);
-        add(sb, "MethodFilter", methodFilter);
-        return sb.toString();
-    }
-
-    private static void add(StringBuilder sb, String name, Object filter) {
-        if (filter != null) {
-            sb.append(' ');
-            sb.append(name);
-            sb.append('=');
-            if (filter instanceof Object[]) {
-                sb.append(Arrays.toString((Object[]) filter));
-            } else {
-                sb.append(String.valueOf(filter));
-            }
-        }
-    }
-
-    @Override
-    public RuntimeException interceptException(Throwable e) {
-        if (e instanceof BailoutException && !Options.InterceptBailout.getValue(options)) {
-            return null;
-        }
-        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()) {
-            // Only dump a context object once.
-            if (!firstSeen.containsKey(o)) {
-                firstSeen.put(o, o);
-                if (Options.DumpOnError.getValue(options) || Options.Dump.getValue(options) != null) {
-                    Debug.dump(Debug.BASIC_LEVEL, o, "Exception: %s", e);
-                } else {
-                    Debug.log("Context obj %s", o);
-                }
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public Collection<DebugDumpHandler> dumpHandlers() {
-        return dumpHandlers;
-    }
-
-    @Override
-    public Collection<DebugVerifyHandler> verifyHandlers() {
-        return verifyHandlers;
-    }
-
-    @Override
-    public void addToContext(Object o) {
-        extraFilters.add(o);
-    }
-
-    @Override
-    public void removeFromContext(Object o) {
-        extraFilters.remove(o);
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Indent.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Indent.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,7 +23,7 @@
 package org.graalvm.compiler.debug;
 
 /**
- * Object used to close a debug {@link Debug#indent() indentation} scope.
+ * Object used to close a debug {@link DebugContext#indent() indentation} scope.
  * <p>
  * Example usage:
  *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/JavaMethodContext.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/JavaMethodContext.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,8 +25,8 @@
 import jdk.vm.ci.meta.JavaMethod;
 
 /**
- * Interface for objects used in Debug {@linkplain Debug#context() context} that can provide a
- * {@link JavaMethod}.
+ * Interface for objects used in Debug {@linkplain DebugContext#context() context} that can provide
+ * a {@link JavaMethod}.
  */
 public interface JavaMethodContext {
     JavaMethod asJavaMethod();
--- /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/KeyRegistry.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.graalvm.util.EconomicMap;
+
+/**
+ * Registry for allocating a globally unique integer id to each {@link AbstractKey}.
+ */
+public class KeyRegistry {
+
+    private static final EconomicMap<String, Integer> keyMap = EconomicMap.create();
+    private static final List<AbstractKey> keys = new ArrayList<>();
+
+    /**
+     * Ensures a given metric key is registered.
+     *
+     * @return the globally unique id for {@code value}
+     */
+    static synchronized int register(AbstractKey key) {
+        String name = key.getName();
+        if (!keyMap.containsKey(name)) {
+            keyMap.put(name, keys.size());
+            keys.add(key);
+        }
+        return keyMap.get(name);
+    }
+
+    /**
+     * Gets a copy of the registered keys.
+     *
+     * @return a list where {@code get(i).getIndex() == i}
+     */
+    public static synchronized List<MetricKey> getKeys() {
+        return new ArrayList<>(keys);
+    }
+}
--- /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/MemUseTrackerKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import com.sun.management.ThreadMXBean;
+
+/**
+ * Tracks memory usage within a scope using {@link ThreadMXBean}. This facility should be employed
+ * using the try-with-resources pattern:
+ *
+ * <pre>
+ * try (DebugCloseable a = memUseTracker.start()) {
+ *     // the code to measure
+ * }
+ * </pre>
+ */
+public interface MemUseTrackerKey extends MetricKey {
+
+    /**
+     * Creates a point from which memory usage will be recorded if memory use tracking is
+     * {@linkplain DebugContext#isMemUseTrackingEnabled() enabled}.
+     *
+     * @return an object that must be closed once the activity has completed to add the memory used
+     *         since this call to the total for this tracker
+     */
+    DebugCloseable start(DebugContext debug);
+
+    /**
+     * Gets the current value of this tracker.
+     */
+    long getCurrentValue(DebugContext debug);
+
+    @Override
+    MemUseTrackerKey doc(String string);
+
+    static long getCurrentThreadAllocatedBytes() {
+        return Management.getCurrentThreadAllocatedBytes();
+    }
+}
--- /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/MemUseTrackerKeyImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import static org.graalvm.compiler.debug.DebugCloseable.VOID_CLOSEABLE;
+
+import org.graalvm.util.Pair;
+
+class MemUseTrackerKeyImpl extends AccumulatedKey implements MemUseTrackerKey {
+
+    MemUseTrackerKeyImpl(String format, Object arg1, Object arg2) {
+        super(new FlatMemUseTracker(format, arg1, arg2), format, arg1, arg2);
+    }
+
+    @Override
+    public DebugCloseable start(DebugContext debug) {
+        if (debug.isMemUseTrackerEnabled(this)) {
+            CloseableCounter result = new MemUseCloseableCounterImpl(this, debug);
+            debug.currentMemUseTracker = result;
+            return result;
+        }
+        return VOID_CLOSEABLE;
+    }
+
+    public static String valueToString(long value) {
+        return String.format("%d bytes", value);
+    }
+
+    @Override
+    public String toHumanReadableFormat(long value) {
+        return valueToString(value);
+    }
+
+    static final class FlatMemUseTracker extends AbstractKey implements MetricKey {
+
+        FlatMemUseTracker(String nameFormat, Object nameArg1, Object nameArg2) {
+            super(nameFormat, nameArg1, nameArg2);
+        }
+
+        @Override
+        protected String createName(String format, Object arg1, Object arg2) {
+            return super.createName(format, arg1, arg2) + FLAT_KEY_SUFFIX;
+        }
+
+        @Override
+        public MetricKey doc(String doc) {
+            throw new IllegalArgumentException("Cannot set documentation for derived key " + getName());
+        }
+
+        @Override
+        public String getDocName() {
+            return null;
+        }
+
+        @Override
+        public String toHumanReadableFormat(long value) {
+            return valueToString(value);
+        }
+
+        @Override
+        public Pair<String, String> toCSVFormat(long value) {
+            return Pair.create(String.valueOf(value), "bytes");
+        }
+    }
+
+    static final class MemUseCloseableCounterImpl extends CloseableCounter implements DebugCloseable {
+
+        private final DebugContext debug;
+
+        MemUseCloseableCounterImpl(AccumulatedKey counter, DebugContext debug) {
+            super(debug, debug.currentMemUseTracker, counter);
+            this.debug = debug;
+        }
+
+        @Override
+        long getCounterValue() {
+            return MemUseTrackerKey.getCurrentThreadAllocatedBytes();
+        }
+
+        @Override
+        public void close() {
+            super.close();
+            debug.currentMemUseTracker = parent;
+        }
+    }
+
+    @Override
+    public Pair<String, String> toCSVFormat(long value) {
+        return Pair.create(String.valueOf(value), "bytes");
+    }
+
+    @Override
+    public MemUseTrackerKey doc(String doc) {
+        setDoc(doc);
+        return this;
+    }
+}
--- /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/MetricKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.util.Comparator;
+
+import org.graalvm.util.Pair;
+
+/**
+ * A key for a metric.
+ */
+public interface MetricKey {
+
+    /**
+     * Converts a given value for this key to a string, scaling it to a more useful unit of
+     * measurement and appending a suffix indicating the unit where applicable. This representation
+     * is intended for human consumption.
+     */
+    String toHumanReadableFormat(long value);
+
+    /**
+     * Converts a given value for this key to a CSV format intended for automated data processing.
+     *
+     * @param value
+     * @return a pair where first is the {@code value} with any scaling conversion applied and
+     *         second is the unit of measurement used for the first component (this will be the
+     *         empty string for a simple counter)
+     */
+    Pair<String, String> toCSVFormat(long value);
+
+    /**
+     * Gets the name of this key.
+     */
+    String getName();
+
+    /**
+     * Comparator to sort keys by their names.
+     */
+    Comparator<MetricKey> NAME_COMPARATOR = new Comparator<MetricKey>() {
+
+        @Override
+        public int compare(MetricKey o1, MetricKey o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
+
+    };
+
+    /**
+     * Sets the documentation for this key.
+     */
+    MetricKey doc(String string);
+
+    /**
+     * Gets the name to use when listing keys. Note that this may be different from
+     * {@link #getName()}.
+     *
+     * @return {@code null} if this key is derived from another key and so should not be listed
+     */
+    String getDocName();
+
+    /**
+     * Gets the documentation for this key.
+     *
+     * @return {@code null} if this key has no documentation
+     */
+    String getDoc();
+}
--- /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/ScopeImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+
+import org.graalvm.compiler.debug.DebugContext.DisabledScope;
+
+import jdk.vm.ci.meta.JavaMethod;
+
+public final class ScopeImpl implements DebugContext.Scope {
+
+    private final class IndentImpl implements Indent {
+
+        private static final String INDENTATION_INCREMENT = "  ";
+
+        final String indent;
+        final IndentImpl parentIndent;
+
+        IndentImpl(IndentImpl parentIndent) {
+            this.parentIndent = parentIndent;
+            this.indent = (parentIndent == null ? "" : parentIndent.indent + INDENTATION_INCREMENT);
+        }
+
+        private boolean logScopeName() {
+            return logScopeName;
+        }
+
+        private void printScopeName(StringBuilder str, boolean isCurrent) {
+            if (logScopeName) {
+                boolean parentPrinted = false;
+                if (parentIndent != null) {
+                    parentPrinted = parentIndent.logScopeName();
+                    parentIndent.printScopeName(str, false);
+                }
+                /*
+                 * Always print the current scope, scopes with context and the any scope whose
+                 * parent didn't print. This ensure the first new scope always shows up.
+                 */
+                if (isCurrent || printContext(null) != 0 || !parentPrinted) {
+                    str.append(indent).append("[thread:").append(Thread.currentThread().getId()).append("] scope: ").append(getQualifiedName()).append(System.lineSeparator());
+                }
+                printContext(str);
+                logScopeName = false;
+            }
+        }
+
+        /**
+         * Print or count the context objects for the current scope.
+         */
+        private int printContext(StringBuilder str) {
+            int count = 0;
+            if (context != null && context.length > 0) {
+                // Include some context in the scope output
+                for (Object contextObj : context) {
+                    if (contextObj instanceof JavaMethodContext || contextObj instanceof JavaMethod) {
+                        if (str != null) {
+                            str.append(indent).append("Context: ").append(contextObj).append(System.lineSeparator());
+                        }
+                        count++;
+                    }
+                }
+            }
+            return count;
+        }
+
+        public void log(int logLevel, String msg, Object... args) {
+            if (isLogEnabled(logLevel)) {
+                StringBuilder str = new StringBuilder();
+                printScopeName(str, true);
+                str.append(indent);
+                String result = args.length == 0 ? msg : String.format(msg, args);
+                String lineSep = System.lineSeparator();
+                str.append(result.replace(lineSep, lineSep.concat(indent)));
+                str.append(lineSep);
+                output.append(str);
+                lastUsedIndent = this;
+            }
+        }
+
+        IndentImpl indent() {
+            lastUsedIndent = new IndentImpl(this);
+            return lastUsedIndent;
+        }
+
+        @Override
+        public void close() {
+            if (parentIndent != null) {
+                lastUsedIndent = parentIndent;
+            }
+        }
+    }
+
+    private final DebugContext owner;
+    private final ScopeImpl parent;
+    private final boolean sandbox;
+    private IndentImpl lastUsedIndent;
+    private boolean logScopeName;
+
+    private final Object[] context;
+
+    private String qualifiedName;
+    private final String unqualifiedName;
+
+    private static final char SCOPE_SEP = '.';
+
+    private boolean countEnabled;
+    private boolean timeEnabled;
+    private boolean memUseTrackingEnabled;
+    private boolean verifyEnabled;
+
+    private int currentDumpLevel;
+    private int currentLogLevel;
+
+    private PrintStream output;
+    private boolean interceptDisabled;
+
+    static final Object[] EMPTY_CONTEXT = new Object[0];
+
+    ScopeImpl(DebugContext owner, Thread thread) {
+        this(owner, thread.getName(), null, false);
+    }
+
+    ScopeImpl(DebugContext owner, String unqualifiedName, ScopeImpl parent, boolean sandbox, Object... context) {
+        this.owner = owner;
+        this.parent = parent;
+        this.sandbox = sandbox;
+        this.context = context;
+        this.unqualifiedName = unqualifiedName;
+        if (parent != null) {
+            logScopeName = !unqualifiedName.equals("");
+            this.interceptDisabled = parent.interceptDisabled;
+        } else {
+            logScopeName = true;
+        }
+
+        this.output = TTY.out;
+        assert context != null;
+    }
+
+    @Override
+    public void close() {
+        owner.currentScope = parent;
+        owner.lastClosedScope = this;
+    }
+
+    public boolean isDumpEnabled(int dumpLevel) {
+        assert dumpLevel >= 0;
+        return currentDumpLevel >= dumpLevel;
+    }
+
+    public boolean isVerifyEnabled() {
+        return verifyEnabled;
+    }
+
+    public boolean isLogEnabled(int logLevel) {
+        assert logLevel > 0;
+        return currentLogLevel >= logLevel;
+    }
+
+    public boolean isCountEnabled() {
+        return countEnabled;
+    }
+
+    public boolean isTimeEnabled() {
+        return timeEnabled;
+    }
+
+    public boolean isMemUseTrackingEnabled() {
+        return memUseTrackingEnabled;
+    }
+
+    public void log(int logLevel, String msg, Object... args) {
+        assert owner.checkNoConcurrentAccess();
+        if (isLogEnabled(logLevel)) {
+            getLastUsedIndent().log(logLevel, msg, args);
+        }
+    }
+
+    public void dump(int dumpLevel, Object object, String formatString, Object... args) {
+        assert isDumpEnabled(dumpLevel);
+        if (isDumpEnabled(dumpLevel)) {
+            DebugConfig config = getConfig();
+            if (config != null) {
+                for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
+                    dumpHandler.dump(owner, object, formatString, args);
+                }
+            }
+        }
+    }
+
+    private DebugConfig getConfig() {
+        return owner.currentConfig;
+    }
+
+    /**
+     * @see DebugContext#verify(Object, String)
+     */
+    public void verify(Object object, String formatString, Object... args) {
+        if (isVerifyEnabled()) {
+            DebugConfig config = getConfig();
+            if (config != null) {
+                String message = String.format(formatString, args);
+                for (DebugVerifyHandler handler : config.verifyHandlers()) {
+                    handler.verify(owner, object, message);
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates and enters a new scope which is either a child of the current scope or a disjoint top
+     * level scope.
+     *
+     * @param name the name of the new scope
+     * @param sandboxConfig the configuration to use for a new top level scope, or null if the new
+     *            scope should be a child scope
+     * @param newContextObjects objects to be appended to the debug context
+     * @return the new scope which will be exited when its {@link #close()} method is called
+     */
+    public ScopeImpl scope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
+        ScopeImpl newScope = null;
+        if (sandboxConfig != null) {
+            newScope = new ScopeImpl(owner, name.toString(), this, true, newContextObjects);
+        } else {
+            newScope = this.createChild(name.toString(), newContextObjects);
+        }
+        newScope.updateFlags(owner.currentConfig);
+        return newScope;
+    }
+
+    @SuppressWarnings({"unchecked", "unused"})
+    private static <E extends Exception> RuntimeException silenceException(Class<E> type, Throwable ex) throws E {
+        throw (E) ex;
+    }
+
+    public RuntimeException handle(Throwable e) {
+        try {
+            if (owner.lastClosedScope instanceof ScopeImpl) {
+                ScopeImpl lastClosed = (ScopeImpl) owner.lastClosedScope;
+                assert lastClosed.parent == this : "DebugContext.handle() used without closing a scope opened by DebugContext.scope(...) or DebugContext.sandbox(...) " +
+                                "or an exception occurred while opening a scope";
+                if (e != owner.lastExceptionThrown) {
+                    RuntimeException newException = null;
+                    // Make the scope in which the exception was thrown
+                    // the current scope again.
+                    owner.currentScope = lastClosed;
+
+                    // When this try block exits, the above action will be undone
+                    try (ScopeImpl s = lastClosed) {
+                        newException = s.interceptException(e);
+                    }
+
+                    // Checks that the action really is undone
+                    assert owner.currentScope == this;
+                    assert lastClosed == owner.lastClosedScope;
+
+                    if (newException == null) {
+                        owner.lastExceptionThrown = e;
+                    } else {
+                        owner.lastExceptionThrown = newException;
+                        throw newException;
+                    }
+                }
+            } else if (owner.lastClosedScope == null) {
+                throw new AssertionError("DebugContext.handle() used without closing a scope opened by DebugContext.scope(...) or DebugContext.sandbox(...) " +
+                                "or an exception occurred while opening a scope");
+            } else {
+                assert owner.lastClosedScope instanceof DisabledScope : owner.lastClosedScope;
+            }
+        } catch (Throwable t) {
+            t.initCause(e);
+            throw t;
+        }
+
+        if (e instanceof Error) {
+            throw (Error) e;
+        }
+        if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        }
+        throw silenceException(RuntimeException.class, e);
+    }
+
+    void updateFlags(DebugConfig config) {
+        if (config == null) {
+            countEnabled = false;
+            memUseTrackingEnabled = false;
+            timeEnabled = false;
+            verifyEnabled = false;
+            currentDumpLevel = -1;
+            // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
+            // set while logging
+            output = TTY.out;
+        } else {
+            countEnabled = config.isCountEnabled(this);
+            memUseTrackingEnabled = config.isMemUseTrackingEnabled(this);
+            timeEnabled = config.isTimeEnabled(this);
+            verifyEnabled = config.isVerifyEnabled(this);
+            output = config.output();
+            currentDumpLevel = config.getDumpLevel(this);
+            currentLogLevel = config.getLogLevel(this);
+        }
+    }
+
+    DebugCloseable disableIntercept() {
+        boolean previous = interceptDisabled;
+        interceptDisabled = true;
+        return new DebugCloseable() {
+            @Override
+            public void close() {
+                interceptDisabled = previous;
+            }
+        };
+    }
+
+    @SuppressWarnings("try")
+    private RuntimeException interceptException(final Throwable e) {
+        if (!interceptDisabled && owner.currentConfig != null) {
+            try (ScopeImpl s = scope("InterceptException", null, e)) {
+                return owner.currentConfig.interceptException(owner, e);
+            } catch (Throwable t) {
+                return new RuntimeException("Exception while intercepting exception", t);
+            }
+        }
+        return null;
+    }
+
+    private ScopeImpl createChild(String newName, Object[] newContext) {
+        return new ScopeImpl(owner, newName, this, false, newContext);
+    }
+
+    @Override
+    public Iterable<Object> getCurrentContext() {
+        final ScopeImpl scope = this;
+        return new Iterable<Object>() {
+
+            @Override
+            public Iterator<Object> iterator() {
+                return new Iterator<Object>() {
+
+                    ScopeImpl currentScope = scope;
+                    int objectIndex;
+
+                    @Override
+                    public boolean hasNext() {
+                        selectScope();
+                        return currentScope != null;
+                    }
+
+                    private void selectScope() {
+                        while (currentScope != null && currentScope.context.length <= objectIndex) {
+                            currentScope = currentScope.sandbox ? null : currentScope.parent;
+                            objectIndex = 0;
+                        }
+                    }
+
+                    @Override
+                    public Object next() {
+                        selectScope();
+                        if (currentScope != null) {
+                            return currentScope.context[objectIndex++];
+                        }
+                        throw new IllegalStateException("May only be called if there is a next element.");
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException("This iterator is read only.");
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public String getQualifiedName() {
+        if (qualifiedName == null) {
+            if (parent == null) {
+                qualifiedName = unqualifiedName;
+            } else {
+                qualifiedName = parent.getQualifiedName() + SCOPE_SEP + unqualifiedName;
+            }
+        }
+        return qualifiedName;
+    }
+
+    public Indent pushIndentLogger() {
+        lastUsedIndent = getLastUsedIndent().indent();
+        return lastUsedIndent;
+    }
+
+    public IndentImpl getLastUsedIndent() {
+        if (lastUsedIndent == null) {
+            if (parent != null) {
+                lastUsedIndent = new IndentImpl(parent.getLastUsedIndent());
+            } else {
+                lastUsedIndent = new IndentImpl(null);
+            }
+        }
+        return lastUsedIndent;
+    }
+}
--- /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/TimerKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A timer for some activity of interest. A timer should be deployed using the try-with-resources
+ * pattern:
+ *
+ * <pre>
+ * try (TimerCloseable a = timer.start()) {
+ *     // the code to time
+ * }
+ * </pre>
+ */
+public interface TimerKey extends MetricKey {
+
+    /**
+     * Starts this timer.
+     *
+     * @return an object that must be closed once the activity has completed to add the elapsed time
+     *         since this call to the total for this timer
+     */
+    DebugCloseable start(DebugContext debug);
+
+    /**
+     * Gets the current value of this timer.
+     */
+    long getCurrentValue(DebugContext debug);
+
+    /**
+     * Gets the time unit of this timer.
+     */
+    TimeUnit getTimeUnit();
+
+    @Override
+    TimerKey doc(String string);
+
+    /**
+     * Gets the timer recording the amount time spent within a timed scope minus the time spent in
+     * any nested timed scopes.
+     *
+     * @return null if this timer does not support flat timing
+     */
+    default TimerKey getFlat() {
+        return null;
+    }
+}
--- /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/TimerKeyImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.debug;
+
+import static org.graalvm.compiler.debug.DebugCloseable.VOID_CLOSEABLE;
+
+import java.util.concurrent.TimeUnit;
+
+import org.graalvm.util.Pair;
+
+final class TimerKeyImpl extends AccumulatedKey implements TimerKey {
+    static class FlatTimer extends AbstractKey implements TimerKey {
+        private TimerKeyImpl accm;
+
+        FlatTimer(String nameFormat, Object nameArg1, Object nameArg2) {
+            super(nameFormat, nameArg1, nameArg2);
+        }
+
+        @Override
+        protected String createName(String format, Object arg1, Object arg2) {
+            return super.createName(format, arg1, arg2) + FLAT_KEY_SUFFIX;
+        }
+
+        @Override
+        public String toHumanReadableFormat(long value) {
+            return valueToString(value);
+        }
+
+        @Override
+        public TimeUnit getTimeUnit() {
+            return accm.getTimeUnit();
+        }
+
+        @Override
+        public DebugCloseable start(DebugContext debug) {
+            return accm.start(debug);
+        }
+
+        @Override
+        public Pair<String, String> toCSVFormat(long value) {
+            return TimerKeyImpl.toCSVFormatHelper(value);
+        }
+
+        @Override
+        public TimerKey doc(String doc) {
+            throw new IllegalArgumentException("Cannot set documentation for derived key " + getName());
+        }
+
+        @Override
+        public String getDocName() {
+            return null;
+        }
+    }
+
+    TimerKeyImpl(String nameFormat, Object nameArg1, Object nameArg2) {
+        super(new FlatTimer(nameFormat, nameArg1, nameArg2), nameFormat, nameArg1, nameArg2);
+        ((FlatTimer) flat).accm = this;
+    }
+
+    @Override
+    public DebugCloseable start(DebugContext debug) {
+        if (debug.isTimerEnabled(this)) {
+            Timer result = new Timer(this, debug);
+            debug.currentTimer = result;
+            return result;
+        } else {
+            return VOID_CLOSEABLE;
+        }
+    }
+
+    public static String valueToString(long value) {
+        return String.format("%d.%d ms", value / 1000000, (value / 100000) % 10);
+    }
+
+    @Override
+    public TimerKey getFlat() {
+        return (FlatTimer) flat;
+    }
+
+    @Override
+    public String toHumanReadableFormat(long value) {
+        return valueToString(value);
+    }
+
+    @Override
+    public TimeUnit getTimeUnit() {
+        return TimeUnit.NANOSECONDS;
+    }
+
+    final class Timer extends CloseableCounter implements DebugCloseable {
+        final DebugContext debug;
+
+        Timer(AccumulatedKey counter, DebugContext debug) {
+            super(debug, debug.currentTimer, counter);
+            this.debug = debug;
+        }
+
+        @Override
+        public void close() {
+            super.close();
+            debug.currentTimer = parent;
+        }
+
+        @Override
+        protected long getCounterValue() {
+            return TimeSource.getTimeNS();
+        }
+
+    }
+
+    @Override
+    public Pair<String, String> toCSVFormat(long value) {
+        return toCSVFormatHelper(value);
+    }
+
+    static Pair<String, String> toCSVFormatHelper(long value) {
+        return Pair.create(Long.toString(value / 1000), "us");
+    }
+
+    @Override
+    public TimerKey doc(String doc) {
+        setDoc(doc);
+        return this;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/TopLevelDebugConfig.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 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.debug;
-
-/**
- * A marker class for a scoped debug configuration covering a compilation region. Useful for
- * programmatically enabling debug config features.
- *
- */
-public class TopLevelDebugConfig extends DelegatingDebugConfig {
-}
--- /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/UniquePathUtilities.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 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.debug;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+
+public class UniquePathUtilities {
+
+    private static final AtomicLong globalTimeStamp = new AtomicLong();
+    /**
+     * This generates a per thread persistent id to aid mapping related dump files with each other.
+     */
+    private static final ThreadLocal<PerThreadSequence> threadDumpId = new ThreadLocal<>();
+    private static final AtomicInteger dumpId = new AtomicInteger();
+
+    static class PerThreadSequence {
+        final int threadID;
+        HashMap<String, Integer> sequences = new HashMap<>(2);
+
+        PerThreadSequence(int threadID) {
+            this.threadID = threadID;
+        }
+
+        String generateID(String extension) {
+            Integer box = sequences.get(extension);
+            if (box == null) {
+                sequences.put(extension, 1);
+                return Integer.toString(threadID);
+            } else {
+                sequences.put(extension, box + 1);
+                return Integer.toString(threadID) + '-' + box;
+            }
+        }
+    }
+
+    private static String getThreadDumpId(String extension) {
+        PerThreadSequence id = threadDumpId.get();
+        if (id == null) {
+            id = new PerThreadSequence(dumpId.incrementAndGet());
+            threadDumpId.set(id);
+        }
+        return id.generateID(extension);
+    }
+
+    public static String formatExtension(String ext) {
+        if (ext == null || ext.length() == 0) {
+            return "";
+        }
+        return "." + ext;
+    }
+
+    public static long getGlobalTimeStamp() {
+        if (globalTimeStamp.get() == 0) {
+            globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
+        }
+        return globalTimeStamp.get();
+    }
+
+    /**
+     * Generates a {@link Path} using the format "%s-%d_%d%s" with the {@code baseNameOption}, a
+     * {@link #getGlobalTimeStamp() global timestamp} , {@link #getThreadDumpId a per thread unique
+     * id} and an optional {@code extension}.
+     *
+     * @return the output file path or null if the flag is null
+     */
+    public static Path getPath(OptionValues options, OptionKey<String> option, String extension) throws IOException {
+        return getPath(options, option, extension, true);
+    }
+
+    /**
+     * Generate a {@link Path} using the format "%s-%d_%s" with the {@code baseNameOption}, a
+     * {@link #getGlobalTimeStamp() global timestamp} and an optional {@code extension} .
+     *
+     * @return the output file path or null if the flag is null
+     */
+    public static Path getPathGlobal(OptionValues options, OptionKey<String> baseNameOption, String extension) throws IOException {
+        return getPath(options, baseNameOption, extension, false);
+    }
+
+    private static Path getPath(OptionValues options, OptionKey<String> baseNameOption, String extension, boolean includeThreadId) throws IOException {
+        if (baseNameOption.getValue(options) == null) {
+            return null;
+        }
+        String ext = formatExtension(extension);
+        final String name = includeThreadId
+                        ? String.format("%s-%d_%s%s", baseNameOption.getValue(options), getGlobalTimeStamp(), getThreadDumpId(ext), ext)
+                        : String.format("%s-%d%s", baseNameOption.getValue(options), getGlobalTimeStamp(), ext);
+        Path result = Paths.get(name);
+        if (result.isAbsolute()) {
+            return result;
+        }
+        Path dumpDir = DebugOptions.getDumpDirectory(options);
+        return dumpDir.resolve(name).normalize();
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/AccumulatedDebugValue.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-public abstract class AccumulatedDebugValue extends DebugValue {
-    protected final DebugValue flat;
-
-    public AccumulatedDebugValue(String name, boolean conditional, DebugValue flat) {
-        super(name + "_Accm", conditional);
-        this.flat = flat;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/CloseableCounterImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import org.graalvm.compiler.debug.DebugCloseable;
-
-/**
- * A helper class for DebugValues that can nest and need to split out accumulated and flat values
- * for some kind of counter-like measurement.
- */
-abstract class CloseableCounterImpl implements DebugCloseable {
-
-    protected final CloseableCounterImpl parent;
-    protected final AccumulatedDebugValue counter;
-    protected final long start;
-    protected long nestedAmountToSubtract;
-
-    CloseableCounterImpl(CloseableCounterImpl parent, AccumulatedDebugValue counter) {
-        this.parent = parent;
-        this.start = getCounterValue();
-        this.counter = counter;
-    }
-
-    /**
-     * A hook for subclasses. Lets them perform custom operations with the value since the last
-     * invocation of {@link CloseableCounterImpl#close()} of this accumulated
-     * {@link CloseableCounterImpl#counter}.
-     *
-     * @param difference since the last invocation of this counter flat
-     */
-    protected void interceptDifferenceAccm(long difference) {
-        // hook for subclasses
-    }
-
-    /**
-     * A hook for subclasses. Lets them perform custom operations with the value since the last
-     * invocation of {@link CloseableCounterImpl#close()} of this flat
-     * {@link CloseableCounterImpl#counter}.
-     *
-     * @param difference since the last invocation of this counter flat
-     */
-    protected void interceptDifferenceFlat(long difference) {
-        // hook for subclasses
-    }
-
-    @Override
-    public void close() {
-        long end = getCounterValue();
-        long difference = end - start;
-        if (parent != null) {
-            if (!counter.getName().equals(parent.counter.getName())) {
-                parent.nestedAmountToSubtract += difference;
-                // Look for our counter in an outer scope and fix up
-                // the adjustment to the flat count
-                CloseableCounterImpl ancestor = parent.parent;
-                while (ancestor != null) {
-                    if (ancestor.counter.getName().equals(counter.getName())) {
-                        ancestor.nestedAmountToSubtract -= difference;
-                        break;
-                    }
-                    ancestor = ancestor.parent;
-                }
-            }
-        }
-        long flatAmount = difference - nestedAmountToSubtract;
-        counter.addToCurrentValue(difference);
-        counter.flat.addToCurrentValue(flatAmount);
-        interceptDifferenceAccm(difference);
-        interceptDifferenceFlat(flatAmount);
-    }
-
-    abstract long getCounterValue();
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/CounterImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-
-public abstract class CounterImpl extends DebugValue implements DebugCounter {
-
-    public CounterImpl(String name, boolean conditional) {
-        super(name, conditional);
-        if (isEnabled()) {
-            // Allows for zero counters to be shown
-            getCurrentValue();
-        }
-    }
-
-    @Override
-    public void increment() {
-        add(1);
-    }
-
-    @Override
-    public String rawUnit() {
-        return "";
-    }
-
-    @Override
-    public String toRawString(long value) {
-        return Long.toString(value);
-    }
-
-    @Override
-    public String toString(long value) {
-        return Long.toString(value);
-    }
-
-    private static final class InterceptingCounterImpl extends CounterImpl {
-
-        private InterceptingCounterImpl(String name, boolean conditional) {
-            super(name, conditional);
-        }
-
-        @Override
-        public void add(long value) {
-            if (isEnabled()) {
-                if (Debug.isMethodMeterEnabled()) {
-                    MethodMetricsImpl.addToCurrentScopeMethodMetrics(getName(), value);
-                }
-                super.addToCurrentValue(value);
-            }
-        }
-    }
-
-    private static final class PlainCounterImpl extends CounterImpl {
-
-        private PlainCounterImpl(String name, boolean conditional) {
-            super(name, conditional);
-        }
-
-        @Override
-        public void add(long value) {
-            if (isEnabled()) {
-                super.addToCurrentValue(value);
-            }
-        }
-    }
-
-    public static DebugCounter create(String name, boolean conditional, boolean intercepting) {
-        if (intercepting) {
-            return new InterceptingCounterImpl(name, conditional);
-        }
-        return new PlainCounterImpl(name, conditional);
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramAsciiPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2013, 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.internal;
-
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.List;
-
-import org.graalvm.compiler.debug.DebugHistogram;
-import org.graalvm.compiler.debug.DebugHistogram.CountedValue;
-import org.graalvm.compiler.debug.DebugHistogram.Printer;
-
-/**
- * Renders a textual representation of a histogram to a given print stream.
- */
-public class DebugHistogramAsciiPrinter implements Printer {
-
-    public static final int NumberSize = 10;
-    public static final int DefaultNameSize = 50;
-    public static final int DefaultBarSize = 100;
-    public static final int DefaultScale = 1;
-
-    private final PrintStream os;
-    private final int limit;
-    private final int nameSize;
-    private final int barSize;
-    private final int scale;
-
-    public DebugHistogramAsciiPrinter(PrintStream os) {
-        this(os, Integer.MAX_VALUE, DefaultNameSize, DefaultBarSize, DefaultScale);
-    }
-
-    /**
-     * @param os where to print
-     * @param limit limits printing to the {@code limit} most frequent values
-     * @param nameSize the width of the value names column
-     * @param barSize the width of the value frequency column
-     * @param scale a factor by which every result is divided
-     */
-    public DebugHistogramAsciiPrinter(PrintStream os, int limit, int nameSize, int barSize, int scale) {
-        this.os = os;
-        this.limit = limit;
-        this.nameSize = nameSize;
-        this.barSize = barSize;
-        this.scale = scale;
-    }
-
-    @Override
-    public void print(DebugHistogram histogram) {
-        List<CountedValue> list = histogram.getValues();
-        if (list.isEmpty()) {
-            os.printf("%s is empty.%n", histogram.getName());
-            return;
-        }
-
-        // Sum up the total number of elements.
-        long total = list.stream().mapToLong(CountedValue::getCount).sum();
-
-        // Print header.
-        os.printf("%s has %d unique elements and %d total elements:%n", histogram.getName(), list.size(), total / scale);
-
-        long max = list.get(0).getCount() / scale;
-        final int lineSize = nameSize + NumberSize + barSize + 10;
-        printLine(os, '-', lineSize);
-        String formatString = "| %-" + nameSize + "s | %-" + NumberSize + "d | %-" + barSize + "s |\n";
-        for (int i = 0; i < list.size() && i < limit; ++i) {
-            CountedValue cv = list.get(i);
-            long value = cv.getCount() / scale;
-            char[] bar = new char[(int) (((double) value / (double) max) * barSize)];
-            Arrays.fill(bar, '=');
-            String objectString = String.valueOf(cv.getValue());
-            if (objectString.length() > nameSize) {
-                objectString = objectString.substring(0, nameSize - 3) + "...";
-            }
-            os.printf(formatString, objectString, value, new String(bar));
-        }
-        printLine(os, '-', lineSize);
-    }
-
-    private static void printLine(PrintStream printStream, char c, int lineSize) {
-        char[] charArr = new char[lineSize];
-        Arrays.fill(charArr, c);
-        printStream.printf("%s%n", new String(charArr));
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2013, 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.internal;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-import org.graalvm.compiler.debug.DebugHistogram;
-
-public class DebugHistogramImpl implements DebugHistogram {
-
-    private final String name;
-    private HashMap<Object, CountedValue> map = new HashMap<>();
-
-    public DebugHistogramImpl(String name) {
-        this.name = name;
-    }
-
-    @Override
-    public void add(Object value) {
-        CountedValue cv = map.get(value);
-        if (cv == null) {
-            map.put(value, new CountedValue(1, value));
-        } else {
-            cv.inc();
-        }
-    }
-
-    @Override
-    public void add(Object value, long count) {
-        CountedValue cv = map.get(value);
-        if (cv == null) {
-            map.put(value, new CountedValue(count, value));
-        } else {
-            cv.add(count);
-        }
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public List<CountedValue> getValues() {
-        ArrayList<CountedValue> res = new ArrayList<>(map.values());
-        Collections.sort(res);
-        return res;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugHistogramRPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2013, 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.internal;
-
-import java.io.PrintStream;
-import java.util.List;
-
-import org.graalvm.compiler.debug.DebugHistogram;
-import org.graalvm.compiler.debug.DebugHistogram.CountedValue;
-import org.graalvm.compiler.debug.DebugHistogram.Printer;
-
-/**
- * Renders a histogram as an R script to a given print stream. The R script emitted for a histogram
- * is a simple set of statements for defining a vector of named objects.
- */
-public class DebugHistogramRPrinter implements Printer {
-
-    private PrintStream os;
-    private int limit;
-
-    public DebugHistogramRPrinter(PrintStream os) {
-        this(os, Integer.MAX_VALUE);
-    }
-
-    /**
-     * @param os where to print
-     * @param limit limits printing to the {@code limit} most frequent values
-     */
-    public DebugHistogramRPrinter(PrintStream os, int limit) {
-        this.os = os;
-        this.limit = limit;
-    }
-
-    @Override
-    public void print(DebugHistogram histogram) {
-        List<CountedValue> list = histogram.getValues();
-        if (list.isEmpty()) {
-            return;
-        }
-
-        String var = histogram.getName().replace('-', '.').replace(' ', '_');
-        os.print(var + " <- c(");
-        for (int i = 0; i < list.size() && i < limit; ++i) {
-            CountedValue cv = list.get(i);
-            if (i != 0) {
-                os.print(", ");
-            }
-            os.print(cv.getCount());
-        }
-        os.println(");");
-
-        os.print("names(" + var + ") <- c(");
-        for (int i = 0; i < list.size() && i < limit; ++i) {
-            CountedValue cv = list.get(i);
-            if (i != 0) {
-                os.print(", ");
-            }
-            os.print("\"" + cv.getValue() + "\"");
-        }
-        os.println(");");
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,579 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import java.io.PrintStream;
-import java.util.Iterator;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.DebugVerifyHandler;
-import org.graalvm.compiler.debug.DelegatingDebugConfig;
-import org.graalvm.compiler.debug.Indent;
-import org.graalvm.compiler.debug.JavaMethodContext;
-import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.TopLevelDebugConfig;
-
-import jdk.vm.ci.meta.JavaMethod;
-
-public final class DebugScope implements Debug.Scope {
-
-    private final class IndentImpl implements Indent {
-
-        private static final String INDENTATION_INCREMENT = "  ";
-
-        final String indent;
-        final IndentImpl parentIndent;
-
-        IndentImpl(IndentImpl parentIndent) {
-            this.parentIndent = parentIndent;
-            this.indent = (parentIndent == null ? "" : parentIndent.indent + INDENTATION_INCREMENT);
-        }
-
-        private boolean logScopeName() {
-            return logScopeName;
-        }
-
-        private void printScopeName(StringBuilder str, boolean isCurrent) {
-            if (logScopeName) {
-                boolean parentPrinted = false;
-                if (parentIndent != null) {
-                    parentPrinted = parentIndent.logScopeName();
-                    parentIndent.printScopeName(str, false);
-                }
-                /*
-                 * Always print the current scope, scopes with context and the any scope whose
-                 * parent didn't print. This ensure the first new scope always shows up.
-                 */
-                if (isCurrent || printContext(null) != 0 || !parentPrinted) {
-                    str.append(indent).append("[thread:").append(Thread.currentThread().getId()).append("] scope: ").append(getQualifiedName()).append(System.lineSeparator());
-                }
-                printContext(str);
-                logScopeName = false;
-            }
-        }
-
-        /**
-         * Print or count the context objects for the current scope.
-         */
-        private int printContext(StringBuilder str) {
-            int count = 0;
-            if (context != null && context.length > 0) {
-                // Include some context in the scope output
-                for (Object contextObj : context) {
-                    if (contextObj instanceof JavaMethodContext || contextObj instanceof JavaMethod) {
-                        if (str != null) {
-                            str.append(indent).append("Context: ").append(contextObj).append(System.lineSeparator());
-                        }
-                        count++;
-                    }
-                }
-            }
-            return count;
-        }
-
-        public void log(int logLevel, String msg, Object... args) {
-            if (isLogEnabled(logLevel)) {
-                StringBuilder str = new StringBuilder();
-                printScopeName(str, true);
-                str.append(indent);
-                String result = args.length == 0 ? msg : String.format(msg, args);
-                String lineSep = System.lineSeparator();
-                str.append(result.replace(lineSep, lineSep.concat(indent)));
-                str.append(lineSep);
-                output.append(str);
-                lastUsedIndent = this;
-            }
-        }
-
-        IndentImpl indent() {
-            lastUsedIndent = new IndentImpl(this);
-            return lastUsedIndent;
-        }
-
-        @Override
-        public void close() {
-            if (parentIndent != null) {
-                lastUsedIndent = parentIndent;
-            }
-        }
-    }
-
-    /**
-     * Interface for an additional information object per scope. The information object will be
-     * given to child scopes, but can be explicitly set with
-     * {@link DebugScope#enhanceWithExtraInfo(CharSequence, ExtraInfo, boolean, Object...)}
-     */
-    public interface ExtraInfo {
-
-    }
-
-    private static final ThreadLocal<DebugScope> instanceTL = new ThreadLocal<>();
-    private static final ThreadLocal<DebugScope> lastClosedTL = new ThreadLocal<>();
-    private static final ThreadLocal<DebugConfig> configTL = new ThreadLocal<>();
-    private static final ThreadLocal<Throwable> lastExceptionThrownTL = new ThreadLocal<>();
-
-    private final DebugScope parent;
-    private final DebugConfig parentConfig;
-    private final boolean sandbox;
-    private IndentImpl lastUsedIndent;
-    private boolean logScopeName;
-
-    private final Object[] context;
-
-    private DebugValueMap valueMap;
-
-    private String qualifiedName;
-    private final String unqualifiedName;
-
-    private final ExtraInfo extraInfo;
-
-    private static final AtomicLong uniqueScopeId = new AtomicLong();
-    private final long scopeId;
-
-    private static final char SCOPE_SEP = '.';
-
-    private boolean countEnabled;
-    private boolean timeEnabled;
-    private boolean memUseTrackingEnabled;
-    private boolean verifyEnabled;
-    private boolean methodMetricsEnabled;
-
-    private int currentDumpLevel;
-    private int currentLogLevel;
-
-    private PrintStream output;
-
-    public static long getCurrentGlobalScopeId() {
-        return uniqueScopeId.get();
-    }
-
-    public static DebugScope getInstance() {
-        DebugScope result = instanceTL.get();
-        if (result == null) {
-            DebugScope topLevelDebugScope = new DebugScope(Thread.currentThread());
-            instanceTL.set(topLevelDebugScope);
-            return topLevelDebugScope;
-        } else {
-            return result;
-        }
-    }
-
-    public static DebugConfig getConfig() {
-        return configTL.get();
-    }
-
-    static final Object[] EMPTY_CONTEXT = new Object[0];
-
-    private DebugScope(Thread thread) {
-        this(thread.getName(), null, uniqueScopeId.incrementAndGet(), null, false);
-        computeValueMap(thread.getName());
-        DebugValueMap.registerTopLevel(getValueMap());
-    }
-
-    private DebugScope(String unqualifiedName, DebugScope parent, long scopeId, ExtraInfo metaInfo, boolean sandbox, Object... context) {
-        this.parent = parent;
-        this.sandbox = sandbox;
-        this.parentConfig = getConfig();
-        this.context = context;
-        this.scopeId = scopeId;
-        this.unqualifiedName = unqualifiedName;
-        this.extraInfo = metaInfo;
-        if (parent != null) {
-            logScopeName = !unqualifiedName.equals("");
-        } else {
-            logScopeName = true;
-        }
-
-        this.output = TTY.out;
-        assert context != null;
-    }
-
-    private void computeValueMap(String name) {
-        if (parent != null) {
-            for (DebugValueMap child : parent.getValueMap().getChildren()) {
-                if (child.getName().equals(name)) {
-                    this.valueMap = child;
-                    return;
-                }
-            }
-            this.valueMap = new DebugValueMap(name);
-            parent.getValueMap().addChild(this.valueMap);
-        } else {
-            this.valueMap = new DebugValueMap(name);
-        }
-    }
-
-    @Override
-    public void close() {
-        instanceTL.set(parent);
-        configTL.set(parentConfig);
-        lastClosedTL.set(this);
-    }
-
-    public boolean isDumpEnabled(int dumpLevel) {
-        assert dumpLevel >= 0;
-        return currentDumpLevel >= dumpLevel;
-    }
-
-    /**
-     * Enable dumping at the new {@code dumpLevel} for the remainder of enclosing scopes. This only
-     * works if a {@link TopLevelDebugConfig} was installed at a higher scope.
-     *
-     * @param dumpLevel
-     */
-    public static void setDumpLevel(int dumpLevel) {
-        TopLevelDebugConfig config = fetchTopLevelDebugConfig("setDebugLevel");
-        if (config != null) {
-            config.override(DelegatingDebugConfig.Level.DUMP, dumpLevel);
-            recursiveUpdateFlags();
-        }
-    }
-
-    /**
-     * Enable logging at the new {@code logLevel} for the remainder of enclosing scopes. This only
-     * works if a {@link TopLevelDebugConfig} was installed at a higher scope.
-     *
-     * @param logLevel
-     */
-    public static void setLogLevel(int logLevel) {
-        TopLevelDebugConfig config = fetchTopLevelDebugConfig("setLogLevel");
-        if (config != null) {
-            config.override(DelegatingDebugConfig.Level.LOG, logLevel);
-            config.delegate(DelegatingDebugConfig.Feature.LOG_METHOD);
-            recursiveUpdateFlags();
-        }
-    }
-
-    private static void recursiveUpdateFlags() {
-        DebugScope c = DebugScope.getInstance();
-        while (c != null) {
-            c.updateFlags();
-            c = c.parent;
-        }
-    }
-
-    private static TopLevelDebugConfig fetchTopLevelDebugConfig(String msg) {
-        DebugConfig config = getConfig();
-        if (config instanceof TopLevelDebugConfig) {
-            return (TopLevelDebugConfig) config;
-        } else {
-            if (config == null) {
-                TTY.println("DebugScope.%s ignored because debugging is disabled", msg);
-            } else {
-                TTY.println("DebugScope.%s ignored because top level delegate config missing", msg);
-            }
-            return null;
-        }
-    }
-
-    public boolean isVerifyEnabled() {
-        return verifyEnabled;
-    }
-
-    public boolean isLogEnabled(int logLevel) {
-        assert logLevel > 0;
-        return currentLogLevel >= logLevel;
-    }
-
-    public boolean isCountEnabled() {
-        return countEnabled;
-    }
-
-    public boolean isTimeEnabled() {
-        return timeEnabled;
-    }
-
-    public boolean isMethodMeterEnabled() {
-        return methodMetricsEnabled;
-    }
-
-    public boolean isMemUseTrackingEnabled() {
-        return memUseTrackingEnabled;
-    }
-
-    public void log(int logLevel, String msg, Object... args) {
-        if (isLogEnabled(logLevel)) {
-            getLastUsedIndent().log(logLevel, msg, args);
-        }
-    }
-
-    public ExtraInfo getExtraInfo() {
-        return extraInfo;
-    }
-
-    public long scopeId() {
-        return scopeId;
-    }
-
-    public void dump(int dumpLevel, Object object, String formatString, Object... args) {
-        if (isDumpEnabled(dumpLevel)) {
-            DebugConfig config = getConfig();
-            if (config != null) {
-                for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
-                    dumpHandler.dump(object, formatString, args);
-                }
-            }
-        }
-    }
-
-    /**
-     * This method exists mainly to allow a debugger (e.g., Eclipse) to force dump a graph.
-     */
-    public static void forceDump(Object object, String format, Object... args) {
-        DebugConfig config = getConfig();
-        if (config != null) {
-            for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
-                dumpHandler.dump(object, format, args);
-            }
-        } else {
-            TTY.println("Forced dump ignored because debugging is disabled - use -Dgraal.ForceDebugEnable=true");
-        }
-    }
-
-    /**
-     * @see Debug#verify(Object, String)
-     */
-    public void verify(Object object, String formatString, Object... args) {
-        if (isVerifyEnabled()) {
-            DebugConfig config = getConfig();
-            if (config != null) {
-                for (DebugVerifyHandler handler : config.verifyHandlers()) {
-                    handler.verify(object, formatString, args);
-                }
-            }
-        }
-    }
-
-    /**
-     * Creates and enters a new debug scope which is either a child of the current scope or a
-     * disjoint top level scope.
-     *
-     * @param name the name of the new scope
-     * @param sandboxConfig the configuration to use for a new top level scope, or null if the new
-     *            scope should be a child scope
-     * @param newContextObjects objects to be appended to the debug context
-     * @return the new scope which will be exited when its {@link #close()} method is called
-     */
-    public DebugScope scope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
-        DebugScope newScope = null;
-        if (sandboxConfig != null) {
-            newScope = new DebugScope(name.toString(), this, uniqueScopeId.incrementAndGet(), null, true, newContextObjects);
-            configTL.set(sandboxConfig);
-        } else {
-            newScope = this.createChild(name.toString(), this.extraInfo, newContextObjects);
-        }
-        instanceTL.set(newScope);
-        newScope.updateFlags();
-        return newScope;
-    }
-
-    public DebugScope enhanceWithExtraInfo(CharSequence name, ExtraInfo newInfo, boolean newId, Object... newContext) {
-        DebugScope newScope = createChild(name.toString(), newInfo, newId ? uniqueScopeId.incrementAndGet() : this.scopeId, newContext);
-        instanceTL.set(newScope);
-        newScope.updateFlags();
-        return newScope;
-    }
-
-    public RuntimeException handle(Throwable e) {
-        DebugScope lastClosed = lastClosedTL.get();
-        try {
-            assert lastClosed.parent == this : "Debug.handle() used with no matching Debug.scope(...) or Debug.sandbox(...) " +
-                            "or an exception occurred while opening a scope";
-            if (e != lastExceptionThrownTL.get()) {
-                RuntimeException newException = null;
-                instanceTL.set(lastClosed);
-                try (DebugScope s = lastClosed) {
-                    newException = s.interceptException(e);
-                }
-                assert instanceTL.get() == this;
-                assert lastClosed == lastClosedTL.get();
-                if (newException == null) {
-                    lastExceptionThrownTL.set(e);
-                } else {
-                    lastExceptionThrownTL.set(newException);
-                    throw newException;
-                }
-            }
-        } catch (Throwable t) {
-            t.initCause(e);
-            throw t;
-        }
-        if (e instanceof Error) {
-            throw (Error) e;
-        }
-        if (e instanceof RuntimeException) {
-            throw (RuntimeException) e;
-        }
-        throw new RuntimeException(e);
-    }
-
-    private void updateFlags() {
-        DebugConfig config = getConfig();
-        if (config == null) {
-            countEnabled = false;
-            memUseTrackingEnabled = false;
-            timeEnabled = false;
-            verifyEnabled = false;
-            currentDumpLevel = -1;
-            methodMetricsEnabled = false;
-            // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
-            // set while logging
-            output = TTY.out;
-        } else {
-            countEnabled = config.isCountEnabled();
-            memUseTrackingEnabled = config.isMemUseTrackingEnabled();
-            timeEnabled = config.isTimeEnabled();
-            verifyEnabled = config.isVerifyEnabled();
-            output = config.output();
-            currentDumpLevel = config.getDumpLevel();
-            currentLogLevel = config.getLogLevel();
-            methodMetricsEnabled = config.isMethodMeterEnabled();
-        }
-    }
-
-    @SuppressWarnings("try")
-    private RuntimeException interceptException(final Throwable e) {
-        final DebugConfig config = getConfig();
-        if (config != null) {
-            try (DebugScope s = scope("InterceptException", null, e)) {
-                return config.interceptException(e);
-            } catch (Throwable t) {
-                return new RuntimeException("Exception while intercepting exception", t);
-            }
-        }
-        return null;
-    }
-
-    private DebugValueMap getValueMap() {
-        if (valueMap == null) {
-            computeValueMap(unqualifiedName);
-        }
-        return valueMap;
-    }
-
-    long getCurrentValue(int index) {
-        return getValueMap().getCurrentValue(index);
-    }
-
-    void setCurrentValue(int index, long l) {
-        getValueMap().setCurrentValue(index, l);
-    }
-
-    private DebugScope createChild(String newName, ExtraInfo newInfo, Object[] newContext) {
-        return new DebugScope(newName, this, this.scopeId, newInfo, false, newContext);
-    }
-
-    private DebugScope createChild(String newName, ExtraInfo newInfo, long newId, Object[] newContext) {
-        return new DebugScope(newName, this, newId, newInfo, false, newContext);
-    }
-
-    public Iterable<Object> getCurrentContext() {
-        final DebugScope scope = this;
-        return new Iterable<Object>() {
-
-            @Override
-            public Iterator<Object> iterator() {
-                return new Iterator<Object>() {
-
-                    DebugScope currentScope = scope;
-                    int objectIndex;
-
-                    @Override
-                    public boolean hasNext() {
-                        selectScope();
-                        return currentScope != null;
-                    }
-
-                    private void selectScope() {
-                        while (currentScope != null && currentScope.context.length <= objectIndex) {
-                            currentScope = currentScope.sandbox ? null : currentScope.parent;
-                            objectIndex = 0;
-                        }
-                    }
-
-                    @Override
-                    public Object next() {
-                        selectScope();
-                        if (currentScope != null) {
-                            return currentScope.context[objectIndex++];
-                        }
-                        throw new IllegalStateException("May only be called if there is a next element.");
-                    }
-
-                    @Override
-                    public void remove() {
-                        throw new UnsupportedOperationException("This iterator is read only.");
-                    }
-                };
-            }
-        };
-    }
-
-    public static <T> T call(Callable<T> callable) {
-        try {
-            return callable.call();
-        } catch (Exception e) {
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            } else {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    public void setConfig(DebugConfig newConfig) {
-        configTL.set(newConfig);
-        updateFlags();
-    }
-
-    public String getQualifiedName() {
-        if (qualifiedName == null) {
-            if (parent == null) {
-                qualifiedName = unqualifiedName;
-            } else {
-                qualifiedName = parent.getQualifiedName() + SCOPE_SEP + unqualifiedName;
-            }
-        }
-        return qualifiedName;
-    }
-
-    public Indent pushIndentLogger() {
-        lastUsedIndent = getLastUsedIndent().indent();
-        return lastUsedIndent;
-    }
-
-    public IndentImpl getLastUsedIndent() {
-        if (lastUsedIndent == null) {
-            if (parent != null) {
-                lastUsedIndent = new IndentImpl(parent.getLastUsedIndent());
-            } else {
-                lastUsedIndent = new IndentImpl(null);
-            }
-        }
-        return lastUsedIndent;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugValue.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-/**
- * A name and index for a value managed in a thread local value map. All access to the value is made
- * via a {@link DebugValue} instance.
- */
-public abstract class DebugValue implements Comparable<DebugValue> {
-
-    private final String name;
-    private int index;
-    private boolean conditional;
-
-    protected DebugValue(String name, boolean conditional) {
-        this.name = name;
-        this.index = -1;
-        this.conditional = conditional;
-    }
-
-    public long getCurrentValue() {
-        ensureInitialized();
-        return DebugScope.getInstance().getCurrentValue(index);
-    }
-
-    protected void setCurrentValue(long l) {
-        ensureInitialized();
-        DebugScope.getInstance().setCurrentValue(index, l);
-    }
-
-    public void setConditional(boolean flag) {
-        conditional = flag;
-    }
-
-    public boolean isConditional() {
-        return conditional;
-    }
-
-    private void ensureInitialized() {
-        if (index == -1) {
-            index = KeyRegistry.register(this);
-        }
-    }
-
-    protected void addToCurrentValue(long value) {
-        setCurrentValue(getCurrentValue() + value);
-    }
-
-    /**
-     * Gets the globally unique index for the value represented by this object.
-     */
-    public int getIndex() {
-        ensureInitialized();
-        return index;
-    }
-
-    /**
-     * Gets the globally unique name for the value represented by this object.
-     */
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public int compareTo(DebugValue o) {
-        return name.compareTo(o.name);
-    }
-
-    @Override
-    public String toString() {
-        return name + "@" + index;
-    }
-
-    public abstract String toString(long value);
-
-    /**
-     * The raw unit of the value (e.g. 'us', 'ms'). Use {@code ""} if there is no unit.
-     */
-    public abstract String rawUnit();
-
-    /**
-     * The raw value.
-     */
-    public abstract String toRawString(long value);
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugValueMap.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A node in a tree of {@link DebugValue}s.
- */
-public class DebugValueMap {
-
-    private static final List<DebugValueMap> topLevelMaps = new ArrayList<>();
-
-    private long[] values;
-    private List<DebugValueMap> children;
-    private String name;
-
-    public DebugValueMap(String name) {
-        this.name = name;
-    }
-
-    public void setCurrentValue(int index, long l) {
-        ensureSize(index);
-        values[index] = l;
-    }
-
-    public long getCurrentValue(int index) {
-        ensureSize(index);
-        return values[index];
-    }
-
-    public void clearChildren() {
-        if (children != null) {
-            children.clear();
-        }
-    }
-
-    public void reset() {
-        if (values != null) {
-            Arrays.fill(values, 0L);
-        }
-        if (children != null) {
-            for (DebugValueMap child : children) {
-                child.reset();
-            }
-        }
-    }
-
-    private void ensureSize(int index) {
-        if (values == null) {
-            values = new long[index + 1];
-        }
-        if (values.length <= index) {
-            values = Arrays.copyOf(values, index + 1);
-        }
-    }
-
-    private int capacity() {
-        return (values == null) ? 0 : values.length;
-    }
-
-    public void addChild(DebugValueMap map) {
-        if (children == null) {
-            children = new ArrayList<>(4);
-        }
-        children.add(map);
-    }
-
-    public List<DebugValueMap> getChildren() {
-        if (children == null) {
-            return Collections.emptyList();
-        } else {
-            return Collections.unmodifiableList(children);
-        }
-    }
-
-    public boolean hasChildren() {
-        return children != null && !children.isEmpty();
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    @Override
-    public String toString() {
-        return "DebugValueMap<" + getName() + ">";
-    }
-
-    public static synchronized void registerTopLevel(DebugValueMap map) {
-        topLevelMaps.add(map);
-    }
-
-    public static synchronized List<DebugValueMap> getTopLevelMaps() {
-        return topLevelMaps;
-    }
-
-    public void normalize() {
-        if (hasChildren()) {
-            Map<String, DebugValueMap> occurred = new HashMap<>();
-            for (DebugValueMap map : children) {
-                String mapName = map.getName();
-                if (!occurred.containsKey(mapName)) {
-                    occurred.put(mapName, map);
-                    map.normalize();
-                } else {
-                    occurred.get(mapName).mergeWith(map);
-                    occurred.get(mapName).normalize();
-                }
-            }
-
-            if (occurred.values().size() < children.size()) {
-                // At least one duplicate was found.
-                children.clear();
-                for (DebugValueMap map : occurred.values()) {
-                    addChild(map);
-                    map.normalize();
-                }
-            }
-        }
-    }
-
-    private void mergeWith(DebugValueMap map) {
-        if (map.hasChildren()) {
-            if (hasChildren()) {
-                children.addAll(map.children);
-            } else {
-                children = map.children;
-            }
-            map.children = null;
-        }
-
-        int size = Math.max(this.capacity(), map.capacity());
-        ensureSize(size);
-        for (int i = 0; i < size; ++i) {
-            long curValue = getCurrentValue(i);
-            long otherValue = map.getCurrentValue(i);
-            setCurrentValue(i, curValue + otherValue);
-        }
-    }
-
-    public void group() {
-        if (this.hasChildren()) {
-            List<DebugValueMap> oldChildren = new ArrayList<>(this.children);
-            this.children.clear();
-            for (DebugValueMap map : oldChildren) {
-                mergeWith(map);
-            }
-        }
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugValuesPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,293 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueFile;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueHumanReadable;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueSummary;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueThreadFilter;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.SuppressZeroDebugValues;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.graalvm.compiler.debug.CSVUtil;
-import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.debug.LogStream;
-import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.util.CollectionsUtil;
-
-/**
- * Facility for printing the {@linkplain KeyRegistry#getDebugValues() values} collected across all
- * {@link DebugValueMap#getTopLevelMaps() threads}.
- */
-public class DebugValuesPrinter {
-    private static final String COMPUTER_READABLE_FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%s");
-    private static final char SCOPE_DELIMITER = '.';
-    private final MethodMetricsPrinter mmPrinter;
-
-    public DebugValuesPrinter() {
-        this(null);
-    }
-
-    public DebugValuesPrinter(MethodMetricsPrinter mmPrinter) {
-        this.mmPrinter = mmPrinter;
-    }
-
-    public void printDebugValues(OptionValues options) throws GraalError {
-        TTY.println();
-        TTY.println("<DebugValues>");
-        List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps();
-        List<DebugValue> debugValues = KeyRegistry.getDebugValues();
-        if (debugValues.size() > 0) {
-            try {
-                ArrayList<DebugValue> sortedValues = new ArrayList<>(debugValues);
-                Collections.sort(sortedValues);
-
-                String summary = DebugValueSummary.getValue(options);
-                if (summary == null) {
-                    summary = "Complete";
-                }
-                if (DebugValueThreadFilter.getValue(options) != null && topLevelMaps.size() != 0) {
-                    topLevelMaps = CollectionsUtil.filterToList(topLevelMaps, map -> Pattern.compile(DebugValueThreadFilter.getValue(options)).matcher(map.getName()).find());
-                    if (topLevelMaps.size() == 0) {
-                        TTY.println("Warning: DebugValueThreadFilter=%s eliminated all maps so nothing will be printed", DebugValueThreadFilter.getValue(options));
-                    }
-                }
-                switch (summary) {
-                    case "Name": {
-                        LogStream log = getLogStream(options);
-                        printSummary(options, log, topLevelMaps, sortedValues);
-                        break;
-                    }
-                    case "Partial": {
-                        DebugValueMap globalMap = new DebugValueMap("Global");
-                        for (DebugValueMap map : topLevelMaps) {
-                            flattenChildren(map, globalMap);
-                        }
-                        globalMap.normalize();
-                        LogStream log = getLogStream(options);
-                        printMap(options, log, new DebugValueScope(null, globalMap), sortedValues);
-                        break;
-                    }
-                    case "Complete": {
-                        DebugValueMap globalMap = new DebugValueMap("Global");
-                        for (DebugValueMap map : topLevelMaps) {
-                            globalMap.addChild(map);
-                        }
-                        globalMap.group();
-                        globalMap.normalize();
-                        LogStream log = getLogStream(options);
-                        printMap(options, log, new DebugValueScope(null, globalMap), sortedValues);
-                        break;
-                    }
-                    case "Thread":
-                        for (DebugValueMap map : topLevelMaps) {
-                            TTY.println("Showing the results for thread: " + map.getName());
-                            map.group();
-                            map.normalize();
-                            LogStream log = getLogStream(options, map.getName().replace(' ', '_'));
-                            printMap(options, log, new DebugValueScope(null, map), sortedValues);
-                        }
-                        break;
-                    default:
-                        throw new GraalError("Unknown summary type: %s", summary);
-                }
-                for (DebugValueMap topLevelMap : topLevelMaps) {
-                    topLevelMap.reset();
-                }
-            } catch (Throwable e) {
-                // Don't want this to change the exit status of the VM
-                PrintStream err = System.err;
-                err.println("Error while printing debug values:");
-                e.printStackTrace();
-            }
-        }
-        if (mmPrinter != null) {
-            mmPrinter.printMethodMetrics(MethodMetricsImpl.collectedMetrics());
-        }
-        TTY.println("</DebugValues>");
-    }
-
-    private static LogStream getLogStream(OptionValues options) {
-        return getLogStream(options, null);
-    }
-
-    private static LogStream getLogStream(OptionValues options, String prefix) {
-        String debugValueFile = DebugValueFile.getValue(options);
-        if (debugValueFile != null) {
-            try {
-                final String fileName;
-                if (prefix != null) {
-                    fileName = prefix + '-' + debugValueFile;
-                } else {
-                    fileName = debugValueFile;
-                }
-                LogStream logStream = new LogStream(new FileOutputStream(fileName));
-                TTY.println("Writing debug values to '%s'", fileName);
-                return logStream;
-            } catch (FileNotFoundException e) {
-                TTY.println("Warning: Could not open debug value log file: %s (defaulting to TTY)", e.getMessage());
-            }
-        }
-        return TTY.out();
-    }
-
-    private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) {
-        globalMap.addChild(map);
-        for (DebugValueMap child : map.getChildren()) {
-            flattenChildren(child, globalMap);
-        }
-        map.clearChildren();
-    }
-
-    private void printSummary(OptionValues options, LogStream log, List<DebugValueMap> topLevelMaps, List<DebugValue> debugValues) {
-        DebugValueMap result = new DebugValueMap("Summary");
-        for (int i = debugValues.size() - 1; i >= 0; i--) {
-            DebugValue debugValue = debugValues.get(i);
-            int index = debugValue.getIndex();
-            long total = collectTotal(topLevelMaps, index);
-            result.setCurrentValue(index, total);
-        }
-        printMap(options, log, new DebugValueScope(null, result), debugValues);
-    }
-
-    private long collectTotal(List<DebugValueMap> maps, int index) {
-        long total = 0;
-        for (int i = 0; i < maps.size(); i++) {
-            DebugValueMap map = maps.get(i);
-            total += map.getCurrentValue(index);
-            total += collectTotal(map.getChildren(), index);
-        }
-        return total;
-    }
-
-    /**
-     * Tracks the scope when printing a {@link DebugValueMap}, allowing "empty" scopes to be
-     * omitted. An empty scope is one in which there are no (nested) non-zero debug values.
-     */
-    static class DebugValueScope {
-
-        final DebugValueScope parent;
-        final int level;
-        final DebugValueMap map;
-        private boolean printed;
-
-        DebugValueScope(DebugValueScope parent, DebugValueMap map) {
-            this.parent = parent;
-            this.map = map;
-            this.level = parent == null ? 0 : parent.level + 1;
-        }
-
-        public void print(LogStream log) {
-            if (!printed) {
-                printed = true;
-                if (parent != null) {
-                    parent.print(log);
-                }
-                printIndent(log, level);
-                log.printf("%s%n", map.getName());
-            }
-        }
-
-        public String toRawString() {
-            return toRaw(new StringBuilder()).toString();
-        }
-
-        private StringBuilder toRaw(StringBuilder stringBuilder) {
-            final StringBuilder sb = (parent == null) ? stringBuilder : parent.toRaw(stringBuilder).append(SCOPE_DELIMITER);
-            return sb.append(map.getName());
-        }
-
-    }
-
-    private void printMap(OptionValues options, LogStream log, DebugValueScope scope, List<DebugValue> debugValues) {
-        if (DebugValueHumanReadable.getValue(options)) {
-            printMapHumanReadable(options, log, scope, debugValues);
-        } else {
-            printMapComputerReadable(options, log, scope, debugValues);
-        }
-    }
-
-    private void printMapComputerReadable(OptionValues options, LogStream log, DebugValueScope scope, List<DebugValue> debugValues) {
-
-        for (DebugValue value : debugValues) {
-            long l = scope.map.getCurrentValue(value.getIndex());
-            if (l != 0 || !SuppressZeroDebugValues.getValue(options)) {
-                CSVUtil.Escape.println(log, COMPUTER_READABLE_FMT, scope.toRawString(), value.getName(), value.toRawString(l), value.rawUnit());
-            }
-        }
-
-        List<DebugValueMap> children = scope.map.getChildren();
-        for (int i = 0; i < children.size(); i++) {
-            DebugValueMap child = children.get(i);
-            printMapComputerReadable(options, log, new DebugValueScope(scope, child), debugValues);
-        }
-    }
-
-    private void printMapHumanReadable(OptionValues options, LogStream log, DebugValueScope scope, List<DebugValue> debugValues) {
-
-        for (DebugValue value : debugValues) {
-            long l = scope.map.getCurrentValue(value.getIndex());
-            if (l != 0 || !SuppressZeroDebugValues.getValue(options)) {
-                scope.print(log);
-                printIndent(log, scope.level + 1);
-                log.println(value.getName() + "=" + value.toString(l));
-            }
-        }
-
-        List<DebugValueMap> children = scope.map.getChildren();
-        for (int i = 0; i < children.size(); i++) {
-            DebugValueMap child = children.get(i);
-            printMapHumanReadable(options, log, new DebugValueScope(scope, child), debugValues);
-        }
-    }
-
-    private static void printIndent(LogStream log, int level) {
-        for (int i = 0; i < level; ++i) {
-            log.print("    ");
-        }
-        log.print("|-> ");
-    }
-
-    public void clearDebugValues() {
-        List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps();
-        List<DebugValue> debugValues = KeyRegistry.getDebugValues();
-        if (debugValues.size() > 0) {
-            for (DebugValueMap map : topLevelMaps) {
-                map.reset();
-            }
-        }
-        if (mmPrinter != null) {
-            MethodMetricsImpl.clearMM();
-        }
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/KeyRegistry.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Registry for allocating a globally unique integer id to each {@link DebugValue}.
- */
-public class KeyRegistry {
-
-    private static final Map<String, Integer> keyMap = new HashMap<>();
-    private static final List<DebugValue> debugValues = new ArrayList<>();
-
-    /**
-     * Ensures a given debug value is registered.
-     *
-     * @return the globally unique id for {@code value}
-     */
-    public static synchronized int register(DebugValue value) {
-        String name = value.getName();
-        if (!keyMap.containsKey(name)) {
-            keyMap.put(name, debugValues.size());
-            debugValues.add(value);
-        }
-        return keyMap.get(name);
-    }
-
-    /**
-     * Gets a immutable view of the registered debug values.
-     *
-     * @return a list where {@code get(i).getIndex() == i}
-     */
-    public static synchronized List<DebugValue> getDebugValues() {
-        return Collections.unmodifiableList(debugValues);
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/MemUseTrackerImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import static org.graalvm.compiler.debug.DebugCloseable.VOID_CLOSEABLE;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.Management;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-
-public class MemUseTrackerImpl extends AccumulatedDebugValue implements DebugMemUseTracker {
-    private final boolean intercepting;
-
-    public static long getCurrentThreadAllocatedBytes() {
-        return Management.getCurrentThreadAllocatedBytes();
-    }
-
-    /**
-     * Records the most recent active tracker.
-     */
-    private static final ThreadLocal<CloseableCounterImpl> currentTracker = new ThreadLocal<>();
-
-    public MemUseTrackerImpl(String name, boolean conditional, boolean intercepting) {
-        super(name, conditional, new DebugValue(name + "_Flat", conditional) {
-
-            @Override
-            public String toString(long value) {
-                return valueToString(value);
-            }
-
-            @Override
-            public String rawUnit() {
-                return "B";
-            }
-
-            @Override
-            public String toRawString(long value) {
-                return Long.toString(value);
-            }
-        });
-        this.intercepting = intercepting;
-    }
-
-    @Override
-    public DebugCloseable start() {
-        if (!isConditional() || Debug.isMemUseTrackingEnabled()) {
-            CloseableCounterImpl result = intercepting ? new MemUseInterceptingCloseableCounterImpl(this) : new MemUseCloseableCounterImpl(this);
-            currentTracker.set(result);
-            return result;
-        } else {
-            return VOID_CLOSEABLE;
-        }
-    }
-
-    public static String valueToString(long value) {
-        return String.format("%d bytes", value);
-    }
-
-    @Override
-    public String toString(long value) {
-        return valueToString(value);
-    }
-
-    private static final class MemUseCloseableCounterImpl extends CloseableCounterImpl implements DebugCloseable {
-
-        private MemUseCloseableCounterImpl(AccumulatedDebugValue counter) {
-            super(currentTracker.get(), counter);
-        }
-
-        @Override
-        long getCounterValue() {
-            return getCurrentThreadAllocatedBytes();
-        }
-
-        @Override
-        public void close() {
-            super.close();
-            currentTracker.set(parent);
-        }
-    }
-
-    private static final class MemUseInterceptingCloseableCounterImpl extends CloseableCounterImpl implements DebugCloseable {
-
-        private MemUseInterceptingCloseableCounterImpl(AccumulatedDebugValue counter) {
-            super(currentTracker.get(), counter);
-        }
-
-        @Override
-        long getCounterValue() {
-            return getCurrentThreadAllocatedBytes();
-        }
-
-        @Override
-        public void close() {
-            super.close();
-            currentTracker.set(parent);
-        }
-
-        @Override
-        protected void interceptDifferenceAccm(long difference) {
-            if (Debug.isMethodMeterEnabled()) {
-                MethodMetricsImpl.addToCurrentScopeMethodMetrics(counter.getName(), difference);
-            }
-        }
-
-        @Override
-        protected void interceptDifferenceFlat(long difference) {
-            if (Debug.isMethodMeterEnabled()) {
-                MethodMetricsImpl.addToCurrentScopeMethodMetrics(counter.flat.getName(), difference);
-            }
-        }
-    }
-
-    @Override
-    public String rawUnit() {
-        return "B";
-    }
-
-    @Override
-    public String toRawString(long value) {
-        return Long.toString(value);
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/TimerImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.debug.internal;
-
-import static org.graalvm.compiler.debug.DebugCloseable.VOID_CLOSEABLE;
-
-import java.util.concurrent.TimeUnit;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.TimeSource;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-
-public final class TimerImpl extends AccumulatedDebugValue implements DebugTimer {
-    private final boolean intercepting;
-
-    /**
-     * Records the most recent active timer.
-     */
-    private static final ThreadLocal<CloseableCounterImpl> currentTimer = new ThreadLocal<>();
-
-    static class FlatTimer extends DebugValue implements DebugTimer {
-        private TimerImpl accm;
-
-        FlatTimer(String name, boolean conditional) {
-            super(name + "_Flat", conditional);
-        }
-
-        @Override
-        public String toString(long value) {
-            return valueToString(value);
-        }
-
-        @Override
-        public TimeUnit getTimeUnit() {
-            return accm.getTimeUnit();
-        }
-
-        @Override
-        public DebugCloseable start() {
-            return accm.start();
-        }
-
-        @Override
-        public String rawUnit() {
-            return "us";
-        }
-
-        @Override
-        public String toRawString(long value) {
-            return valueToRawString(value);
-        }
-    }
-
-    public TimerImpl(String name, boolean conditional, boolean intercepting) {
-        super(name, conditional, new FlatTimer(name, conditional));
-        ((FlatTimer) flat).accm = this;
-        this.intercepting = intercepting;
-    }
-
-    @Override
-    public DebugCloseable start() {
-        if (!isConditional() || Debug.isTimeEnabled()) {
-            AbstractTimer result = intercepting ? new InterceptingTimer(this) : new Timer(this);
-            currentTimer.set(result);
-            return result;
-        } else {
-            return VOID_CLOSEABLE;
-        }
-    }
-
-    public static String valueToString(long value) {
-        return String.format("%d.%d ms", value / 1000000, (value / 100000) % 10);
-    }
-
-    @Override
-    public DebugTimer getFlat() {
-        return (FlatTimer) flat;
-    }
-
-    @Override
-    public String toString(long value) {
-        return valueToString(value);
-    }
-
-    @Override
-    public TimeUnit getTimeUnit() {
-        return TimeUnit.NANOSECONDS;
-    }
-
-    private abstract class AbstractTimer extends CloseableCounterImpl implements DebugCloseable {
-
-        private AbstractTimer(AccumulatedDebugValue counter) {
-            super(currentTimer.get(), counter);
-        }
-
-        @Override
-        public void close() {
-            super.close();
-            currentTimer.set(parent);
-        }
-    }
-
-    private final class Timer extends AbstractTimer {
-
-        private Timer(TimerImpl timer) {
-            super(timer);
-        }
-
-        @Override
-        protected long getCounterValue() {
-            return TimeSource.getTimeNS();
-        }
-
-    }
-
-    private final class InterceptingTimer extends AbstractTimer {
-
-        private InterceptingTimer(TimerImpl timer) {
-            super(timer);
-        }
-
-        @Override
-        protected long getCounterValue() {
-            return TimeSource.getTimeNS();
-        }
-
-        @Override
-        protected void interceptDifferenceAccm(long difference) {
-            if (Debug.isMethodMeterEnabled()) {
-                MethodMetricsImpl.addToCurrentScopeMethodMetrics(counter.getName(), difference);
-            }
-        }
-
-        @Override
-        protected void interceptDifferenceFlat(long difference) {
-            if (Debug.isMethodMeterEnabled()) {
-                MethodMetricsImpl.addToCurrentScopeMethodMetrics(counter.flat.getName(), difference);
-            }
-        }
-    }
-
-    @Override
-    public String rawUnit() {
-        return "us";
-    }
-
-    @Override
-    public String toRawString(long value) {
-        return valueToRawString(value);
-    }
-
-    public static String valueToRawString(long value) {
-        return Long.toString(value / 1000);
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,444 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug.internal.method;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.graalvm.compiler.debug.CSVUtil;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.internal.DebugScope;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public class MethodMetricsImpl implements DebugMethodMetrics {
-
-    /**
-     * A list capturing all method metrics data of all the compiler threads. Every thread registers
-     * a reference to its thread local map of compilation metrics in this list. During metrics
-     * dumping this list is globally locked and all method entries across all threads are merged to
-     * a result.
-     */
-    private static final List<Map<ResolvedJavaMethod, CompilationData>> threadMaps = new ArrayList<>();
-    /**
-     * Every compiler thread carries its own map of metric data for each method and compilation it
-     * compiles. This data is stored in {@link ThreadLocal} maps for each compiler thread that are
-     * merged before metrics are reported. Storing compilation data thread locally reduces the
-     * locking on access of a method metric object to one point for each thread, the first access
-     * where the thread local is initialized.
-     */
-    private static final ThreadLocal<Map<ResolvedJavaMethod, CompilationData>> threadEntries = new ThreadLocal<>();
-    /**
-     * The lowest debug scope id that should be used during metric dumping. When a bootstrap is run
-     * all compilations during bootstrap are also collected if the associated debug filters match.
-     * Data collected during bootstrap should normally not be included in metrics for application
-     * compilation, thus every compilation lower than this index is ignored during metric dumping.
-     */
-    private static long lowestCompilationDebugScopeId;
-
-    public static class CompilationData {
-        /**
-         * A mapping of graph ids (unique ids used for the caching) to compilations.
-         */
-        private final Map<Long, Map<String, Long>> compilations;
-        /**
-         * A pointer to a {@code MethodMetricsImpl} object. This reference is created once for every
-         * compilation of a method (and once for each thread, i.e. if method a is compiled by 8
-         * compiler threads there will be 8 metrics objects for the given method, one local to every
-         * thread, this avoids synchronizing on the metrics object on every access) accessing method
-         * metrics for a given method.
-         */
-        private final MethodMetricsImpl metrics;
-
-        CompilationData(ResolvedJavaMethod method) {
-            compilations = new HashMap<>(8);
-            metrics = new MethodMetricsImpl(method);
-        }
-
-        public Map<Long, Map<String, Long>> getCompilations() {
-            return compilations;
-        }
-    }
-
-    private static void addThreadCompilationData(Map<ResolvedJavaMethod, CompilationData> threadMap) {
-        synchronized (threadMaps) {
-            threadMaps.add(threadMap);
-        }
-    }
-
-    /**
-     * A reference to the {@link ResolvedJavaMethod} method object. This object's identity is used
-     * to store metrics for each compilation.
-     */
-    private final ResolvedJavaMethod method;
-    /**
-     * A list of all recorded compilations. This is generated during metric dumping when all thread
-     * local metrics are merged into one final method metrics object that is than reported
-     */
-    private List<Map<Long, Map<String, Long>>> collected;
-    /**
-     * A pointer to the current compilation data for the {@link MethodMetricsImpl#method} method
-     * which allows to avoid synchronizing over the compilation data. This reference changes for
-     * each compilation of the given method. It is set on the first access of this
-     * {@link MethodMetricsImpl} object during the call to
-     * {@link MethodMetricsImpl#getMethodMetrics(ResolvedJavaMethod)}.
-     */
-    private Map<String, Long> currentCompilation;
-
-    MethodMetricsImpl(ResolvedJavaMethod method) {
-        this.method = method;
-    }
-
-    private static void clearData() {
-        lowestCompilationDebugScopeId = DebugScope.getCurrentGlobalScopeId();
-    }
-
-    @Override
-    public void addToMetric(long value, String metricName) {
-        if (!Debug.isMethodMeterEnabled() || value == 0) {
-            return;
-        }
-        assert metricName != null;
-        Long valueStored = currentCompilation.get(metricName);
-        currentCompilation.put(metricName, valueStored == null ? value : value + valueStored);
-    }
-
-    @Override
-    public long getCurrentMetricValue(String metricName) {
-        assert metricName != null;
-        Long valueStored = currentCompilation.get(metricName);
-        return valueStored == null ? 0 : valueStored;
-    }
-
-    @Override
-    public void addToMetric(long value, String format, Object arg1) {
-        addToMetric(value, String.format(format, arg1));
-    }
-
-    @Override
-    public void addToMetric(long value, String format, Object arg1, Object arg2) {
-        addToMetric(value, String.format(format, arg1, arg2));
-    }
-
-    @Override
-    public void addToMetric(long value, String format, Object arg1, Object arg2, Object arg3) {
-        addToMetric(value, String.format(format, arg1, arg2, arg3));
-    }
-
-    @Override
-    public void incrementMetric(String metricName) {
-        addToMetric(1, metricName);
-    }
-
-    @Override
-    public void incrementMetric(String format, Object arg1) {
-        incrementMetric(String.format(format, arg1));
-    }
-
-    @Override
-    public void incrementMetric(String format, Object arg1, Object arg2) {
-        incrementMetric(String.format(format, arg1, arg2));
-    }
-
-    @Override
-    public void incrementMetric(String format, Object arg1, Object arg2, Object arg3) {
-        incrementMetric(String.format(format, arg1, arg2, arg3));
-    }
-
-    @Override
-    public long getCurrentMetricValue(String format, Object arg1) {
-        return getCurrentMetricValue(String.format(format, arg1));
-    }
-
-    @Override
-    public long getCurrentMetricValue(String format, Object arg1, Object arg2) {
-        return getCurrentMetricValue(String.format(format, arg1, arg2));
-    }
-
-    @Override
-    public long getCurrentMetricValue(String format, Object arg1, Object arg2, Object arg3) {
-        return getCurrentMetricValue(String.format(format, arg1, arg2, arg3));
-    }
-
-    @Override
-    public ResolvedJavaMethod getMethod() {
-        return method;
-    }
-
-    public static DebugMethodMetrics getMethodMetrics(ResolvedJavaMethod method) {
-        assert method != null;
-        Map<ResolvedJavaMethod, CompilationData> threadCache = threadEntries.get();
-        if (threadCache == null) {
-            // this branch will only be executed once for each compiler thread on the first request
-            // of a method metric
-            threadCache = new HashMap<>(GraalDebugConfig.Options.MethodFilter.getValue(DebugScope.getConfig().getOptions()) == null ? 128 : 16);
-            threadEntries.set(threadCache);
-            addThreadCompilationData(threadCache);
-        }
-
-        CompilationData recorded = threadCache.get(method);
-        if (recorded == null) {
-            recorded = new CompilationData(method);
-            threadCache.put(method, recorded);
-        }
-        // pre-generate the current compilation map to avoid doing it later every time we add to a
-        // metric or read a current metric's value
-        long compilationId = DebugScope.getInstance().scopeId();
-        Map<String, Long> currentCompilation = recorded.compilations.get(compilationId);
-        if (currentCompilation == null) {
-            // this map is generated for every distinct compilation of a unique method
-            currentCompilation = new HashMap<>(32);
-            recorded.compilations.put(compilationId, currentCompilation);
-            // we remember a reference to the current compilation to avoid the expensive lookup
-            recorded.metrics.currentCompilation = currentCompilation;
-        }
-
-        return recorded.metrics;
-    }
-
-    public void dumpASCII(PrintStream p) {
-        // we need to lock the threadmap as a concurrent call to #collectedMetrics can change the
-        // content of this#collected
-        synchronized (threadMaps) {
-            String methodName = method.toString();
-            int maxLen = methodName.length();
-            int entrySum = 0;
-            // get the longest entry
-            for (Map<Long, Map<String, Long>> compilationThreadTable : collected) {
-                for (Map.Entry<Long, Map<String, Long>> compilationEntry : compilationThreadTable.entrySet()) {
-                    Map<String, Long> table = compilationEntry.getValue();
-                    if (table != null) {
-                        for (Map.Entry<String, Long> entry : table.entrySet()) {
-                            maxLen = Math.max(maxLen, entry.getKey().length());
-                            entrySum += entry.getValue();
-                        }
-                    }
-                }
-            }
-            if (entrySum == 0) {
-                // nothing to report
-                return;
-            }
-            maxLen += 23;
-            for (int j = 0; j < maxLen; j++) {
-                p.print("#");
-            }
-            p.println();
-            p.println(methodName);
-            for (int j = 0; j < maxLen; j++) {
-                p.print("~");
-            }
-            p.println();
-            for (Map<Long, Map<String, Long>> compilationThreadTable : collected) {
-                for (Map.Entry<Long, Map<String, Long>> compilationEntry : compilationThreadTable.entrySet()) {
-                    Map<String, Long> table = compilationEntry.getValue();
-                    if (table != null) {
-                        if (table.values().stream().filter(x -> x > 0).count() == 0) {
-                            continue;
-                        }
-                        Set<Map.Entry<String, Long>> entries = table.entrySet();
-                        for (Map.Entry<String, Long> entry : entries.stream().sorted((x, y) -> x.getKey().compareTo(y.getKey())).collect(Collectors.toList())) {
-                            long value = entry.getValue();
-                            // report timers in ms and memory in mb
-                            if ((entry.getKey().endsWith("Accm") || entry.getKey().endsWith("Flat")) &&
-                                            !entry.getKey().toLowerCase().contains("mem")) {
-                                value = value / 1000000;
-                            }
-                            if (value == 0) {
-                                continue;
-                            }
-                            p.print(String.format("%-" + String.valueOf(maxLen - 23) + "s = %20d", entry.getKey(), value));
-                            p.println();
-                        }
-                        for (int j = 0; j < maxLen; j++) {
-                            p.print("~");
-                        }
-                        p.println();
-                    }
-                }
-            }
-            for (int j = 0; j < maxLen; j++) {
-                p.print("#");
-            }
-            p.println();
-        }
-    }
-
-    private static final String FMT = CSVUtil.buildFormatString("%s", "%s", "%d", "%d", "%s", "%d");
-
-    public void dumpCSV(PrintStream p) {
-        // we need to lock the threadmap as a concurrent call to #collectedMetrics can change
-        // the content of this#collected
-        synchronized (threadMaps) {
-            String methodName = method.format("%H.%n(%p)%R");
-            /*
-             * NOTE: the caching mechanism works by caching compilation data based on the identity
-             * of the resolved java method object. The identity is based on the metaspace address of
-             * the resolved java method object. If the class was loaded by different class loaders
-             * or e.g. loaded - unloaded - loaded the identity will be different. Therefore we also
-             * need to include the identity in the reporting of the data as it is an additional
-             * dimension to <method,compilationId>.
-             */
-            String methodIdentity = String.valueOf(System.identityHashCode(method));
-            int nrOfCompilations = 0;
-            for (Map<Long, Map<String, Long>> compilationThreadTable : collected) {
-                for (Map.Entry<Long, Map<String, Long>> compilationEntry : compilationThreadTable.entrySet()) {
-                    Map<String, Long> table = compilationEntry.getValue();
-                    if (table != null) {
-                        Set<Map.Entry<String, Long>> entries = table.entrySet();
-                        for (Map.Entry<String, Long> entry : entries.stream().sorted((x, y) -> x.getKey().compareTo(y.getKey())).collect(Collectors.toList())) {
-                            CSVUtil.Escape.println(p, FMT, methodName, methodIdentity, nrOfCompilations, compilationEntry.getKey(), entry.getKey(), entry.getValue());
-                        }
-                        nrOfCompilations++;
-                    }
-                }
-            }
-        }
-    }
-
-    public static Collection<DebugMethodMetrics> collectedMetrics() {
-        synchronized (threadMaps) {
-            // imprecise excluding all compilations that follow, we simply do not report them
-            final long lastId = DebugScope.getCurrentGlobalScopeId();
-            List<DebugMethodMetrics> finalMetrics = new ArrayList<>();
-            Set<ResolvedJavaMethod> methods = new HashSet<>();
-
-            // gather all methods we found
-            threadMaps.forEach(x -> {
-                // snapshot the current compilations to only capture all methods compiled until now
-                HashMap<ResolvedJavaMethod, CompilationData> snapShot = new HashMap<>(x);
-                snapShot.keySet().forEach(y -> methods.add(y));
-            });
-
-            // for each method gather all metrics we want to report
-            for (ResolvedJavaMethod method : methods) {
-                MethodMetricsImpl impl = new MethodMetricsImpl(method);
-                impl.collected = new ArrayList<>();
-                for (Map<ResolvedJavaMethod, CompilationData> threadMap : threadMaps) {
-                    CompilationData threadMethodData = threadMap.get(method);
-
-                    // not every method is necessarily compiled by all threads
-                    if (threadMethodData != null) {
-                        Map<Long, Map<String, Long>> snapshot = new HashMap<>(threadMethodData.compilations);
-                        for (Map.Entry<Long, Map<String, Long>> entry : snapshot.entrySet()) {
-                            if (entry.getKey() < lowestCompilationDebugScopeId || entry.getKey() > lastId) {
-                                entry.setValue(null);
-                            }
-                        }
-                        impl.collected.add(snapshot);
-                    }
-                }
-                finalMetrics.add(impl);
-            }
-
-            return finalMetrics;
-        }
-    }
-
-    public static void clearMM() {
-        clearData();
-    }
-
-    private static final String INLINEE_PREFIX = "INLINING_SCOPE_";
-    private static final boolean TRACK_INLINED_SCOPES = false;
-
-    public static void recordInlinee(ResolvedJavaMethod root, ResolvedJavaMethod caller, ResolvedJavaMethod inlinee) {
-        if (TRACK_INLINED_SCOPES) {
-            Debug.methodMetrics(root).addToMetric(1, "INLINED_METHOD_root: caller:%s inlinee:%s", caller, inlinee);
-        }
-    }
-
-    private static final boolean COUNT_CACHE = false;
-    private static final String HIT_MSG = "InterceptionCache_Hit";
-    private static final String MISS_MSG = "InterceptionCache_Miss";
-    private static final DebugCounter cacheHit = Debug.counter(HIT_MSG);
-    private static final DebugCounter cacheMiss = Debug.counter(MISS_MSG);
-    /**
-     * To avoid the lookup of a method metrics through the
-     * {@link MethodMetricsImpl#getMethodMetrics(ResolvedJavaMethod)} method on every global metric
-     * interception we thread-locally cache the last (through metric interception)
-     * {@link MethodMetricsImpl} object. This avoids additional map lookups and replaces them with a
-     * {@link DebugScope#scopeId()} call and a numerical comparison in a cache hit case.
-     */
-    private static final ThreadLocal<Long> interceptionCache = new ThreadLocal<>();
-    private static final ThreadLocal<MethodMetricsImpl> interceptionMetrics = new ThreadLocal<>();
-
-    public static void addToCurrentScopeMethodMetrics(String metricName, long value) {
-        if (COUNT_CACHE) {
-            if (metricName.equals(HIT_MSG) || metricName.equals(MISS_MSG)) {
-                return;
-            }
-        }
-        final DebugScope currScope = DebugScope.getInstance();
-        final DebugScope.ExtraInfo metaInfo = currScope.getExtraInfo();
-        final long currScopeId = currScope.scopeId();
-        if (metaInfo instanceof MethodMetricsRootScopeInfo) {
-            ResolvedJavaMethod rootMethod = ((MethodMetricsRootScopeInfo) metaInfo).getRootMethod();
-            if (metaInfo instanceof MethodMetricsInlineeScopeInfo) {
-                /*
-                 * if we make use of a method filter(s) together with interception we get a problem
-                 * with inlined methods and their scopes. Inlining will put the inlinee(s) on the
-                 * debug scope context thus Debug.areMethodMetricsEnabled() will yield true if an
-                 * inlinee matches a method filter. Thus we must make sure the root is defined as
-                 * this means the root matched a method filter and therefore the inlinee can be
-                 * safely recorded.
-                 */
-                if (TRACK_INLINED_SCOPES) {
-                    if (threadEntries.get().get(rootMethod) != null) {
-                        Debug.methodMetrics(rootMethod).addToMetric(value, "%s%s", INLINEE_PREFIX, metricName);
-                    }
-                }
-            } else {
-                // when unboxing the thread local on access it must not be null
-                Long cachedId = interceptionCache.get();
-                if (cachedId != null && cachedId == currScopeId) {
-                    interceptionMetrics.get().addToMetric(value, metricName);
-                    if (COUNT_CACHE) {
-                        cacheHit.increment();
-                    }
-                } else {
-                    // avoid the lookup over Debug.methodMetrics
-                    final MethodMetricsImpl impl = (MethodMetricsImpl) getMethodMetrics(rootMethod);
-                    impl.addToMetric(value, metricName);
-                    // cache for next access
-                    interceptionCache.set(currScopeId);
-                    interceptionMetrics.set(impl);
-                    if (COUNT_CACHE) {
-                        cacheMiss.increment();
-                    }
-                }
-            }
-        }
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsInlineeScopeInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug.internal.method;
-
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.debug.internal.DebugScope.ExtraInfo;
-import org.graalvm.compiler.options.OptionValues;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public class MethodMetricsInlineeScopeInfo extends MethodMetricsRootScopeInfo {
-
-    MethodMetricsInlineeScopeInfo(ResolvedJavaMethod rootMethod) {
-        super(rootMethod);
-    }
-
-    public static MethodMetricsInlineeScopeInfo create(ResolvedJavaMethod rootMethod, OptionValues options) {
-        if (GraalDebugConfig.isGlobalMetricsInterceptedByMethodMetricsEnabled(options)) {
-            return new MethodMetricsInlineeScopeInfo(rootMethod);
-        }
-        return null;
-    }
-
-    public static MethodMetricsInlineeScopeInfo create(OptionValues options) {
-        if (GraalDebugConfig.isGlobalMetricsInterceptedByMethodMetricsEnabled(options)) {
-            ExtraInfo rootInfo = DebugScope.getInstance().getExtraInfo();
-            if (rootInfo instanceof MethodMetricsRootScopeInfo) {
-                return new MethodMetricsInlineeScopeInfo(((MethodMetricsRootScopeInfo) rootInfo).getRootMethod());
-            }
-        }
-        return null;
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug.internal.method;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import org.graalvm.compiler.debug.DebugMethodMetrics;
-import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.options.Option;
-import org.graalvm.compiler.options.OptionKey;
-import org.graalvm.compiler.options.OptionType;
-import org.graalvm.compiler.options.OptionValues;
-
-/**
- * Interface for printing a collection of method metrics (e.g. during shutdown).
- */
-public interface MethodMetricsPrinter {
-
-    class Options {
-        // @formatter:off
-        @Option(help = "Dump method metrics to stdout on shutdown.", type = OptionType.Debug)
-        public static final OptionKey<Boolean> MethodMeterPrintAscii = new OptionKey<>(false);
-        @Option(help = "Dump method metrics to the given file in CSV format on shutdown.", type = OptionType.Debug)
-        public static final OptionKey<String> MethodMeterFile = new OptionKey<>(null);
-        // @formatter:on
-    }
-
-    static boolean methodMetricsDumpingEnabled(OptionValues options) {
-        return MethodMetricsPrinter.Options.MethodMeterPrintAscii.getValue(options) || MethodMetricsPrinter.Options.MethodMeterFile.getValue(options) != null;
-    }
-
-    /**
-     * Prints the metrics to a destination specified by the implementor of this interface.
-     *
-     * @param metrics the set of collected method metrics during execution as defined by
-     *            {@link DebugMethodMetrics}.
-     */
-    void printMethodMetrics(Collection<DebugMethodMetrics> metrics);
-
-    class MethodMetricsASCIIPrinter implements MethodMetricsPrinter {
-        private final OutputStream out;
-
-        public MethodMetricsASCIIPrinter(OutputStream out) {
-            this.out = out;
-        }
-
-        @Override
-        public void printMethodMetrics(Collection<DebugMethodMetrics> metrics) {
-            PrintStream p = new PrintStream(out);
-            for (DebugMethodMetrics m : metrics) {
-                ((MethodMetricsImpl) m).dumpASCII(p);
-                p.println();
-            }
-        }
-
-    }
-
-    class MethodMetricsCompositePrinter implements MethodMetricsPrinter {
-        private final List<MethodMetricsPrinter> printers;
-
-        public MethodMetricsCompositePrinter(MethodMetricsPrinter... p) {
-            printers = Arrays.asList(p);
-        }
-
-        public void registerPrinter(MethodMetricsPrinter printer) {
-            printers.add(printer);
-        }
-
-        public void unregisterPrinter(MethodMetricsPrinter printer) {
-            printers.remove(printer);
-        }
-
-        @Override
-        public void printMethodMetrics(Collection<DebugMethodMetrics> metrics) {
-            for (MethodMetricsPrinter p : printers) {
-                p.printMethodMetrics(metrics);
-            }
-        }
-
-    }
-
-    class MethodMetricsCSVFilePrinter implements MethodMetricsPrinter {
-        private FileOutputStream fw;
-
-        public MethodMetricsCSVFilePrinter() {
-            try {
-                fw = new FileOutputStream(new File(Options.MethodMeterFile.getValue(DebugScope.getConfig().getOptions())));
-            } catch (IOException e) {
-                TTY.println("Cannot create file %s for method metrics dumping:%s", Options.MethodMeterFile.getValue(DebugScope.getConfig().getOptions()), e);
-                throw new Error(e);
-            }
-        }
-
-        @Override
-        public void printMethodMetrics(Collection<DebugMethodMetrics> metrics) {
-            // mm printing creates simple (R-parsable) csv files in long data format
-            if (fw != null && metrics != null) {
-                try (PrintStream p = new PrintStream(fw)) {
-                    for (DebugMethodMetrics m : metrics) {
-                        if (m instanceof MethodMetricsImpl) {
-                            ((MethodMetricsImpl) m).dumpCSV(p);
-                            p.println();
-                        }
-                    }
-                }
-                try {
-                    fw.close();
-                } catch (IOException e) {
-                    throw new Error(e);
-                }
-            }
-        }
-
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/method/MethodMetricsRootScopeInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2015, 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.debug.internal.method;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.internal.DebugScope;
-import org.graalvm.compiler.debug.internal.DebugScope.ExtraInfo;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public class MethodMetricsRootScopeInfo implements ExtraInfo {
-    protected final ResolvedJavaMethod rootMethod;
-
-    MethodMetricsRootScopeInfo(ResolvedJavaMethod rootMethod) {
-        this.rootMethod = rootMethod;
-    }
-
-    public ResolvedJavaMethod getRootMethod() {
-        return rootMethod;
-    }
-
-    public static MethodMetricsRootScopeInfo create(ResolvedJavaMethod rootMethod) {
-        return new MethodMetricsRootScopeInfo(rootMethod);
-    }
-
-    /**
-     * Creates and returns a {@link org.graalvm.compiler.debug.Debug.Scope scope} iff there is no
-     * existing {@linkplain org.graalvm.compiler.debug.internal.DebugScope.ExtraInfo extraInfo}
-     * object of type {@link MethodMetricsRootScopeInfo} present in the current {@link DebugScope
-     * scope}.
-     *
-     * @param method
-     * @return a new {@link org.graalvm.compiler.debug.Debug.Scope scope} or {@code null} iff there
-     *         is already an existing one on the scope
-     */
-    public static Debug.Scope createRootScopeIfAbsent(ResolvedJavaMethod method) {
-        if (Debug.isEnabled()) {
-            /*
-             * if the current compilation is not triggered from JVMCI we need a valid context root
-             * method for method metrics
-             */
-            return DebugScope.getInstance().getExtraInfo() instanceof MethodMetricsRootScopeInfo ? null
-                            : Debug.methodMetricsScope("GraalCompilerRoot", MethodMetricsRootScopeInfo.create(method), true);
-        } else {
-            return null;
-        }
-    }
-
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/GraphTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/GraphTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,11 +23,41 @@
 package org.graalvm.compiler.graph.test;
 
 import org.graalvm.compiler.api.test.Graal;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.options.OptionValues;
+import org.junit.After;
 
 public abstract class GraphTest {
 
     static OptionValues getOptions() {
         return Graal.getRequiredCapability(OptionValues.class);
     }
+
+    private final ThreadLocal<DebugContext> cachedDebug = new ThreadLocal<>();
+
+    protected DebugContext getDebug(OptionValues options) {
+        DebugContext cached = cachedDebug.get();
+        if (cached != null) {
+            if (cached.getOptions() == options) {
+                return cached;
+            }
+            throw new AssertionError("At most one " + DebugContext.class.getName() + " object should be created per test");
+        }
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+        cachedDebug.set(debug);
+        return debug;
+    }
+
+    protected DebugContext getDebug() {
+        return getDebug(getOptions());
+    }
+
+    @After
+    public void afterTest() {
+        DebugContext cached = cachedDebug.get();
+        if (cached != null) {
+            cached.closeDumpHandlers(true);
+        }
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeMapTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeMapTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -37,6 +37,7 @@
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.options.OptionValues;
 
 public class NodeMapTest extends GraphTest {
 
@@ -58,7 +59,8 @@
         // Need to initialize HotSpotGraalRuntime before any Node class is initialized.
         Graal.getRuntime();
 
-        graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        graph = new Graph(options, getDebug(options));
         for (int i = 0; i < nodes.length; i++) {
             nodes[i] = graph.add(new TestNode());
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeUsagesTests.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeUsagesTests.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,6 +36,7 @@
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.options.OptionValues;
 
 public class NodeUsagesTests extends GraphTest {
 
@@ -66,7 +67,8 @@
 
     @Test
     public void testReplaceAtUsages() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -95,7 +97,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicateAll() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -124,7 +127,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicateNone() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -153,7 +157,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate1() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -184,7 +189,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate2() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -215,7 +221,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate0() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -246,7 +253,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate02() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -277,7 +285,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate023() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -311,7 +320,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate013() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -345,7 +355,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate203() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -379,7 +390,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate01() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
@@ -410,7 +422,8 @@
 
     @Test
     public void testReplaceAtUsagesWithPredicate12() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         Def def0 = graph.add(new Def());
         Def def1 = graph.add(new Def());
         Use use0 = graph.add(new Use(def0, null, null));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeValidationChecksTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeValidationChecksTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -51,7 +51,7 @@
 
     @Test
     public void testInputNotAlive() {
-        Graph graph = new Graph(getOptions());
+        Graph graph = new Graph(getOptions(), getDebug());
         TestNode node = new TestNode(null, null);
         try {
             graph.add(new TestNode(node, null));
@@ -64,7 +64,7 @@
 
     @Test
     public void testSuccessorNotAlive() {
-        Graph graph = new Graph(getOptions());
+        Graph graph = new Graph(getOptions(), getDebug());
         TestNode node = new TestNode(null, null);
         try {
             graph.add(new TestNode(null, node));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -62,8 +62,8 @@
 
     @Test
     public void singleNodeTest() {
-        getOptions();
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(new TestNode("a"));
         assertTrue(graph.hasNode(TestNode.TYPE));
         assertEquals("a", toString(graph.getNodes(TestNode.TYPE)));
@@ -76,7 +76,8 @@
     @Test
     public void deletingNodeTest() {
         TestNode testNode = new TestNode("a");
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(testNode);
         testNode.safeDelete();
         assertEquals("", toString(graph.getNodes(TestNode.TYPE)));
@@ -85,7 +86,8 @@
     @Test
     public void deleteAndAddTest() {
         TestNode testNode = new TestNode("b");
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(new TestNode("a"));
         graph.add(testNode);
         testNode.safeDelete();
@@ -96,7 +98,8 @@
 
     @Test
     public void iteratorBehaviorTest() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(new TestNode("a"));
         Iterator<TestNode> iterator = graph.getNodes(TestNode.TYPE).iterator();
         assertTrue(iterator.hasNext());
@@ -115,7 +118,8 @@
 
     @Test
     public void complicatedIterationTest() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(new TestNode("a"));
         for (TestNode tn : graph.getNodes(TestNode.TYPE)) {
             String name = tn.getName();
@@ -154,7 +158,8 @@
 
     @Test
     public void addingNodeDuringIterationTest() {
-        Graph graph = new Graph(getOptions());
+        OptionValues options = getOptions();
+        Graph graph = new Graph(options, getDebug(options));
         graph.add(new TestNode("a"));
         StringBuilder sb = new StringBuilder();
         int z = 0;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest2.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/TypedNodeIteratorTest2.java	Fri Jul 07 09:40:47 2017 -0700
@@ -99,7 +99,7 @@
 
     @Test
     public void simpleSubclassTest() {
-        Graph graph = new Graph(getOptions());
+        Graph graph = new Graph(getOptions(), getDebug());
         graph.add(new NodeB("b"));
         graph.add(new NodeD("d"));
 
@@ -109,7 +109,7 @@
 
     @Test
     public void addingNodeDuringIterationTest() {
-        Graph graph = new Graph(getOptions());
+        Graph graph = new Graph(getOptions(), getDebug());
         graph.add(new NodeB("b1"));
         NodeD d1 = graph.add(new NodeD("d1"));
         StringBuilder sb = new StringBuilder();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/CachedGraph.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/CachedGraph.java	Fri Jul 07 09:40:47 2017 -0700
@@ -62,7 +62,9 @@
     @SuppressWarnings("unchecked")
     public G getMutableCopy(Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback) {
         if (!hasMutableCopy()) {
-            mutableCopy = (G) readonlyCopy.copy(duplicationMapCallback);
+            // Sharing the debug context with the copy is safe since both graphs are
+            // only used in the current thread.
+            mutableCopy = (G) readonlyCopy.copy(duplicationMapCallback, readonlyCopy.getDebug());
         }
         return mutableCopy;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,19 @@
  */
 package org.graalvm.compiler.graph;
 
-import org.graalvm.compiler.debug.Debug;
+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.Iterator;
+import java.util.function.Consumer;
+
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.graph.Node.ValueNumberable;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.options.Option;
@@ -37,14 +45,6 @@
 import org.graalvm.util.Equivalence;
 import org.graalvm.util.UnmodifiableEconomicMap;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.function.Consumer;
-
-import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
-import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
-
 /**
  * This class is a graph container, it contains the set of nodes that belong to this graph.
  */
@@ -147,6 +147,11 @@
      */
     private final OptionValues options;
 
+    /**
+     * The {@link DebugContext} used while compiling this graph.
+     */
+    private final DebugContext debug;
+
     private class NodeSourcePositionScope implements DebugCloseable {
         private final NodeSourcePosition previous;
 
@@ -156,6 +161,11 @@
         }
 
         @Override
+        public DebugContext getDebug() {
+            return debug;
+        }
+
+        @Override
         public void close() {
             currentNodeSourcePosition = previous;
         }
@@ -217,8 +227,8 @@
     /**
      * Creates an empty Graph with no name.
      */
-    public Graph(OptionValues options) {
-        this(null, options);
+    public Graph(OptionValues options, DebugContext debug) {
+        this(null, options, debug);
     }
 
     /**
@@ -239,12 +249,14 @@
      *
      * @param name the name of the graph, used for debugging purposes
      */
-    public Graph(String name, OptionValues options) {
+    public Graph(String name, OptionValues options, DebugContext debug) {
         nodes = new Node[INITIAL_NODES_SIZE];
         iterableNodesFirst = new ArrayList<>(NodeClass.allocatedNodeIterabledIds());
         iterableNodesLast = new ArrayList<>(NodeClass.allocatedNodeIterabledIds());
         this.name = name;
         this.options = options;
+        assert debug != null;
+        this.debug = debug;
 
         if (isModificationCountsEnabled()) {
             nodeModCounts = new int[INITIAL_NODES_SIZE];
@@ -302,27 +314,37 @@
 
     /**
      * Creates a copy of this graph.
+     *
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
      */
-    public final Graph copy() {
-        return copy(name, null);
+    public final Graph copy(DebugContext debugForCopy) {
+        return copy(name, null, debugForCopy);
     }
 
     /**
      * Creates a copy of this graph.
      *
      * @param duplicationMapCallback consumer of the duplication map created during the copying
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
      */
-    public final Graph copy(Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback) {
-        return copy(name, duplicationMapCallback);
+    public final Graph copy(Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) {
+        return copy(name, duplicationMapCallback, debugForCopy);
     }
 
     /**
      * Creates a copy of this graph.
      *
      * @param newName the name of the copy, used for debugging purposes (can be null)
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
      */
-    public final Graph copy(String newName) {
-        return copy(newName, null);
+    public final Graph copy(String newName, DebugContext debugForCopy) {
+        return copy(newName, null, debugForCopy);
     }
 
     /**
@@ -330,9 +352,12 @@
      *
      * @param newName the name of the copy, used for debugging purposes (can be null)
      * @param duplicationMapCallback consumer of the duplication map created during the copying
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
      */
-    protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback) {
-        Graph copy = new Graph(newName, options);
+    protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) {
+        Graph copy = new Graph(newName, options, debugForCopy);
         UnmodifiableEconomicMap<Node, Node> duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), (EconomicMap<Node, Node>) null);
         if (duplicationMapCallback != null) {
             duplicationMapCallback.accept(duplicates);
@@ -344,6 +369,10 @@
         return options;
     }
 
+    public DebugContext getDebug() {
+        return debug;
+    }
+
     @Override
     public String toString() {
         return name == null ? super.toString() : "Graph " + name;
@@ -806,7 +835,7 @@
 
     }
 
-    private static final DebugCounter GraphCompressions = Debug.counter("GraphCompressions");
+    private static final CounterKey GraphCompressions = DebugContext.counter("GraphCompressions");
 
     /**
      * If the {@linkplain Options#GraphCompressionThreshold compression threshold} is met, the list
@@ -814,7 +843,7 @@
      * preserving the ordering between the nodes within the list.
      */
     public boolean maybeCompress() {
-        if (Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()) {
+        if (debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()) {
             return false;
         }
         int liveNodeCount = getNodeCount();
@@ -823,7 +852,7 @@
         if (compressionThreshold == 0 || liveNodePercent >= compressionThreshold) {
             return false;
         }
-        GraphCompressions.increment();
+        GraphCompressions.increment(debug);
         int nextId = 0;
         for (int i = 0; nextId < liveNodeCount; i++) {
             Node n = nodes[i];
@@ -1077,7 +1106,7 @@
     }
 
     /**
-     * Adds duplicates of the nodes in {@code nodes} to this graph. This will recreate any edges
+     * Adds duplicates of the nodes in {@code newNodes} to this graph. This will recreate any edges
      * between the duplicate nodes. The {@code replacement} map can be used to replace a node from
      * the source graph by a given node (which must already be in this graph). Edges between
      * duplicate and replacement nodes will also be recreated so care should be taken regarding the
@@ -1118,11 +1147,11 @@
 
     }
 
-    private static final DebugTimer DuplicateGraph = Debug.timer("DuplicateGraph");
+    private static final TimerKey DuplicateGraph = DebugContext.timer("DuplicateGraph");
 
     @SuppressWarnings({"all", "try"})
     public EconomicMap<Node, Node> addDuplicates(Iterable<? extends Node> newNodes, final Graph oldGraph, int estimatedNodeCount, DuplicationReplacement replacements) {
-        try (DebugCloseable s = DuplicateGraph.start()) {
+        try (DebugCloseable s = DuplicateGraph.start(getDebug())) {
             return NodeClass.addGraphDuplicate(this, oldGraph, estimatedNodeCount, newNodes, replacements);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,6 +43,7 @@
 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.DebugContext;
 import org.graalvm.compiler.graph.Graph.NodeEventListener;
 import org.graalvm.compiler.graph.Graph.Options;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
@@ -263,6 +264,13 @@
     }
 
     /**
+     * Gets the debug context associated with this node's graph.
+     */
+    public final DebugContext getDebug() {
+        return graph.getDebug();
+    }
+
+    /**
      * Returns an {@link NodeIterable iterable} which can be used to traverse all non-null input
      * edges of this node.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java	Fri Jul 07 09:40:47 2017 -0700
@@ -45,11 +45,11 @@
 import org.graalvm.compiler.core.common.FieldIntrospection;
 import org.graalvm.compiler.core.common.Fields;
 import org.graalvm.compiler.core.common.FieldsScanner;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.graph.Edges.Type;
 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
 import org.graalvm.compiler.graph.Node.EdgeVisitor;
@@ -79,13 +79,13 @@
 public final class NodeClass<T> extends FieldIntrospection<T> {
 
     // Timers for creation of a NodeClass instance
-    private static final DebugTimer Init_FieldScanning = Debug.timer("NodeClass.Init.FieldScanning");
-    private static final DebugTimer Init_FieldScanningInner = Debug.timer("NodeClass.Init.FieldScanning.Inner");
-    private static final DebugTimer Init_AnnotationParsing = Debug.timer("NodeClass.Init.AnnotationParsing");
-    private static final DebugTimer Init_Edges = Debug.timer("NodeClass.Init.Edges");
-    private static final DebugTimer Init_Data = Debug.timer("NodeClass.Init.Data");
-    private static final DebugTimer Init_AllowedUsages = Debug.timer("NodeClass.Init.AllowedUsages");
-    private static final DebugTimer Init_IterableIds = Debug.timer("NodeClass.Init.IterableIds");
+    private static final TimerKey Init_FieldScanning = DebugContext.timer("NodeClass.Init.FieldScanning");
+    private static final TimerKey Init_FieldScanningInner = DebugContext.timer("NodeClass.Init.FieldScanning.Inner");
+    private static final TimerKey Init_AnnotationParsing = DebugContext.timer("NodeClass.Init.AnnotationParsing");
+    private static final TimerKey Init_Edges = DebugContext.timer("NodeClass.Init.Edges");
+    private static final TimerKey Init_Data = DebugContext.timer("NodeClass.Init.Data");
+    private static final TimerKey Init_AllowedUsages = DebugContext.timer("NodeClass.Init.AllowedUsages");
+    private static final TimerKey Init_IterableIds = DebugContext.timer("NodeClass.Init.IterableIds");
 
     public static final long MAX_EDGES = 8;
     public static final long MAX_LIST_EDGES = 6;
@@ -94,8 +94,8 @@
     public static final long NEXT_EDGE = 0x08;
 
     @SuppressWarnings("try")
-    private static <T extends Annotation> T getAnnotationTimed(AnnotatedElement e, Class<T> annotationClass) {
-        try (DebugCloseable s = Init_AnnotationParsing.start()) {
+    private static <T extends Annotation> T getAnnotationTimed(AnnotatedElement e, Class<T> annotationClass, DebugContext debug) {
+        try (DebugCloseable s = Init_AnnotationParsing.start(debug)) {
             return e.getAnnotation(annotationClass);
         }
     }
@@ -144,8 +144,7 @@
     private final long inputsIteration;
     private final long successorIteration;
 
-    private static final DebugCounter ITERABLE_NODE_TYPES = Debug.counter("IterableNodeTypes");
-    private final DebugCounter nodeIterableCount;
+    private static final CounterKey ITERABLE_NODE_TYPES = DebugContext.counter("IterableNodeTypes");
 
     /**
      * Determines if this node type implements {@link Canonicalizable}.
@@ -172,6 +171,7 @@
     @SuppressWarnings("try")
     public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, FieldsScanner.CalcOffset calcOffset, int[] presetIterableIds, int presetIterableId) {
         super(clazz);
+        DebugContext debug = DebugContext.forCurrentThread();
         this.superNodeClass = superNodeClass;
         assert NODE_CLASS.isAssignableFrom(clazz);
 
@@ -183,18 +183,18 @@
 
         this.isSimplifiable = Simplifiable.class.isAssignableFrom(clazz);
 
-        NodeFieldsScanner fs = new NodeFieldsScanner(calcOffset, superNodeClass);
-        try (DebugCloseable t = Init_FieldScanning.start()) {
+        NodeFieldsScanner fs = new NodeFieldsScanner(calcOffset, superNodeClass, debug);
+        try (DebugCloseable t = Init_FieldScanning.start(debug)) {
             fs.scan(clazz, clazz.getSuperclass(), false);
         }
 
-        try (DebugCloseable t1 = Init_Edges.start()) {
+        try (DebugCloseable t1 = Init_Edges.start(debug)) {
             successors = new SuccessorEdges(fs.directSuccessors, fs.successors);
             successorIteration = computeIterationMask(successors.type(), successors.getDirectCount(), successors.getOffsets());
             inputs = new InputEdges(fs.directInputs, fs.inputs);
             inputsIteration = computeIterationMask(inputs.type(), inputs.getDirectCount(), inputs.getOffsets());
         }
-        try (DebugCloseable t1 = Init_Data.start()) {
+        try (DebugCloseable t1 = Init_Data.start(debug)) {
             data = new Fields(fs.data);
         }
 
@@ -208,7 +208,7 @@
         canGVN = Node.ValueNumberable.class.isAssignableFrom(clazz);
         startGVNNumber = clazz.getName().hashCode();
 
-        NodeInfo info = getAnnotationTimed(clazz, NodeInfo.class);
+        NodeInfo info = getAnnotationTimed(clazz, NodeInfo.class, debug);
         assert info != null : "Missing NodeInfo annotation on " + clazz;
         if (!info.nameTemplate().isEmpty()) {
             this.nameTemplate = info.nameTemplate();
@@ -218,7 +218,7 @@
             this.nameTemplate = "";
         }
 
-        try (DebugCloseable t1 = Init_AllowedUsages.start()) {
+        try (DebugCloseable t1 = Init_AllowedUsages.start(debug)) {
             allowedUsageTypes = superNodeClass == null ? EnumSet.noneOf(InputType.class) : superNodeClass.allowedUsageTypes.clone();
             allowedUsageTypes.addAll(Arrays.asList(info.allowedUsageTypes()));
         }
@@ -227,8 +227,8 @@
             this.iterableIds = presetIterableIds;
             this.iterableId = presetIterableId;
         } else if (IterableNodeType.class.isAssignableFrom(clazz)) {
-            ITERABLE_NODE_TYPES.increment();
-            try (DebugCloseable t1 = Init_IterableIds.start()) {
+            ITERABLE_NODE_TYPES.increment(debug);
+            try (DebugCloseable t1 = Init_IterableIds.start(debug)) {
                 this.iterableId = nextIterableId.getAndIncrement();
 
                 NodeClass<?> snc = superNodeClass;
@@ -243,10 +243,9 @@
             this.iterableId = Node.NOT_ITERABLE;
             this.iterableIds = null;
         }
-        nodeIterableCount = Debug.counter("NodeIterable_%s", clazz);
         assert verifyIterableIds();
 
-        try (Debug.Scope scope = Debug.scope("NodeCosts")) {
+        try (DebugContext.Scope scope = debug.scope("NodeCosts")) {
             /*
              * Note: We do not check for the existence of the node cost annotations during
              * construction as not every node needs to have them set. However if costs are queried,
@@ -271,9 +270,8 @@
                 size = s;
             }
             assert size != null;
-            Debug.log("Node cost for node of type __| %s |_, cycles:%s,size:%s", clazz, cycles, size);
+            debug.log("Node cost for node of type __| %s |_, cycles:%s,size:%s", clazz, cycles, size);
         }
-
     }
 
     private final NodeCycles cycles;
@@ -357,8 +355,7 @@
         return new Fields[]{data, inputs, successors};
     }
 
-    public int[] iterableIds() {
-        nodeIterableCount.increment();
+    int[] iterableIds() {
         return iterableIds;
     }
 
@@ -451,9 +448,11 @@
         public final ArrayList<EdgeInfo> successors = new ArrayList<>();
         int directInputs;
         int directSuccessors;
+        final DebugContext debug;
 
-        protected NodeFieldsScanner(FieldsScanner.CalcOffset calc, NodeClass<?> superNodeClass) {
+        protected NodeFieldsScanner(FieldsScanner.CalcOffset calc, NodeClass<?> superNodeClass, DebugContext debug) {
             super(calc);
+            this.debug = debug;
             if (superNodeClass != null) {
                 translateInto(superNodeClass.inputs, inputs);
                 translateInto(superNodeClass.successors, successors);
@@ -466,10 +465,10 @@
         @SuppressWarnings("try")
         @Override
         protected void scanField(Field field, long offset) {
-            Input inputAnnotation = getAnnotationTimed(field, Node.Input.class);
-            OptionalInput optionalInputAnnotation = getAnnotationTimed(field, Node.OptionalInput.class);
-            Successor successorAnnotation = getAnnotationTimed(field, Successor.class);
-            try (DebugCloseable s = Init_FieldScanningInner.start()) {
+            Input inputAnnotation = getAnnotationTimed(field, Node.Input.class, debug);
+            OptionalInput optionalInputAnnotation = getAnnotationTimed(field, Node.OptionalInput.class, debug);
+            Successor successorAnnotation = getAnnotationTimed(field, Successor.class, debug);
+            try (DebugCloseable s = Init_FieldScanningInner.start(debug)) {
                 Class<?> type = field.getType();
                 int modifiers = field.getModifiers();
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,7 +27,7 @@
 import java.util.NoSuchElementException;
 import java.util.Queue;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 
 public abstract class NodeWorkList implements Iterable<Node> {
 
@@ -75,6 +75,7 @@
         private static final int EXPLICIT_BITMAP_THRESHOLD = 10;
         protected NodeBitMap inQueue;
 
+        private final DebugContext debug;
         private int iterationLimit;
         private Node firstNoChange;
         private Node lastPull;
@@ -82,6 +83,7 @@
 
         public IterativeNodeWorkList(Graph graph, boolean fill, int iterationLimitPerNode) {
             super(graph, fill);
+            debug = graph.getDebug();
             assert iterationLimitPerNode > 0;
             long limit = (long) iterationLimitPerNode * graph.getNodeCount();
             iterationLimit = (int) Long.min(Integer.MAX_VALUE, limit);
@@ -94,7 +96,7 @@
                 public boolean hasNext() {
                     dropDeleted();
                     if (iterationLimit <= 0) {
-                        Debug.log(Debug.INFO_LEVEL, "Exceeded iteration limit in IterativeNodeWorkList");
+                        debug.log(DebugContext.INFO_LEVEL, "Exceeded iteration limit in IterativeNodeWorkList");
                         return false;
                     }
                     return !worklist.isEmpty();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotBackend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotBackend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -226,7 +226,7 @@
         HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null);
 
         DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget());
-        CompilationResultBuilder crb = factory.createBuilder(getCodeCache(), getForeignCalls(), frameMap, masm, dataBuilder, frameContext, lir.getOptions(), compilationResult);
+        CompilationResultBuilder crb = factory.createBuilder(getCodeCache(), getForeignCalls(), frameMap, masm, dataBuilder, frameContext, lir.getOptions(), lir.getDebug(), compilationResult);
         crb.setTotalFrameSize(frameMap.totalFrameSize());
         crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize());
         StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,6 +30,7 @@
 
 import org.graalvm.compiler.asm.Label;
 import org.graalvm.compiler.asm.aarch64.AArch64Address.AddressingMode;
+import org.graalvm.compiler.asm.aarch64.AArch64Assembler.PrefetchMode;
 import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag;
 import org.graalvm.compiler.core.aarch64.AArch64ArithmeticLIRGenerator;
 import org.graalvm.compiler.core.aarch64.AArch64LIRGenerator;
@@ -250,7 +251,7 @@
 
     @Override
     public void emitPrefetchAllocate(Value address) {
-        append(new AArch64PrefetchOp(asAddressValue(address), config.allocatePrefetchInstr));
+        append(new AArch64PrefetchOp(asAddressValue(address), PrefetchMode.PSTL1KEEP));
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLoweringProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLoweringProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,6 +24,7 @@
 package org.graalvm.compiler.hotspot.aarch64;
 
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
@@ -53,10 +54,10 @@
     }
 
     @Override
-    public void initialize(OptionValues options, HotSpotProviders providers, GraalHotSpotVMConfig config) {
-        integerArithmeticSnippets = new AArch64IntegerArithmeticSnippets(options, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
-        floatArithmeticSnippets = new AArch64FloatArithmeticSnippets(options, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
-        super.initialize(options, providers, config);
+    public void initialize(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, GraalHotSpotVMConfig config) {
+        integerArithmeticSnippets = new AArch64IntegerArithmeticSnippets(options, factories, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
+        floatArithmeticSnippets = new AArch64FloatArithmeticSnippets(options, factories, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
+        super.initialize(options, factories, providers, config);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,19 +22,18 @@
  */
 package org.graalvm.compiler.hotspot.aarch64;
 
-import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 import static jdk.vm.ci.aarch64.AArch64.lr;
 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
 import static jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig.fp;
 import static jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig.inlineCacheRegister;
 import static jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig.metaspaceMethodRegister;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 
 import org.graalvm.compiler.core.aarch64.AArch64NodeLIRBuilder;
 import org.graalvm.compiler.core.aarch64.AArch64NodeMatchRules;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
 import org.graalvm.compiler.core.gen.DebugInfoBuilder;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder;
 import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
 import org.graalvm.compiler.hotspot.HotSpotLockStack;
@@ -171,7 +170,7 @@
     @Override
     public void visitFullInfopointNode(FullInfopointNode i) {
         if (i.getState() != null && i.getState().bci == BytecodeFrame.AFTER_BCI) {
-            Debug.log("Ignoring InfopointNode for AFTER_BCI");
+            i.getDebug().log("Ignoring InfopointNode for AFTER_BCI");
         } else {
             super.visitFullInfopointNode(i);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java	Fri Jul 07 09:40:47 2017 -0700
@@ -35,8 +35,8 @@
 import org.graalvm.compiler.core.common.NumUtil;
 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.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.nodes.GraalHotSpotVMConfigNode;
@@ -55,7 +55,7 @@
 
 public class AMD64HotSpotAddressLowering extends AMD64AddressLowering {
 
-    private static final DebugCounter counterFoldedUncompressDuringAddressLowering = Debug.counter("FoldedUncompressDuringAddressLowering");
+    private static final CounterKey counterFoldedUncompressDuringAddressLowering = DebugContext.counter("FoldedUncompressDuringAddressLowering");
 
     private final long heapBase;
     private final Register heapBaseRegister;
@@ -93,25 +93,25 @@
     }
 
     @Override
-    protected boolean improve(AMD64AddressNode addr) {
+    protected boolean improve(DebugContext debug, AMD64AddressNode addr) {
 
         boolean result = false;
 
-        while (super.improve(addr)) {
+        while (super.improve(debug, addr)) {
             result = true;
         }
 
         if (addr.getScale() == Scale.Times1) {
             if (addr.getIndex() instanceof CompressionNode) {
                 if (improveUncompression(addr, (CompressionNode) addr.getIndex(), addr.getBase())) {
-                    counterFoldedUncompressDuringAddressLowering.increment();
+                    counterFoldedUncompressDuringAddressLowering.increment(debug);
                     return true;
                 }
             }
 
             if (addr.getBase() instanceof CompressionNode) {
                 if (improveUncompression(addr, (CompressionNode) addr.getBase(), addr.getIndex())) {
-                    counterFoldedUncompressDuringAddressLowering.increment();
+                    counterFoldedUncompressDuringAddressLowering.increment(debug);
                     return true;
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotBackend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotBackend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,13 @@
  */
 package org.graalvm.compiler.hotspot.amd64;
 
-import static org.graalvm.compiler.core.common.GraalOptions.CanOmitFrame;
-import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
-import static org.graalvm.compiler.core.common.GraalOptions.ZapStackOnMethodEntry;
 import static jdk.vm.ci.amd64.AMD64.r10;
 import static jdk.vm.ci.amd64.AMD64.rax;
 import static jdk.vm.ci.amd64.AMD64.rsp;
 import static jdk.vm.ci.code.ValueUtil.asRegister;
+import static org.graalvm.compiler.core.common.GraalOptions.CanOmitFrame;
+import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
+import static org.graalvm.compiler.core.common.GraalOptions.ZapStackOnMethodEntry;
 
 import org.graalvm.compiler.asm.Assembler;
 import org.graalvm.compiler.asm.Label;
@@ -37,10 +37,11 @@
 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.amd64.AMD64NodeMatchRules;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.LIRKind;
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.target.Backend;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotDataBuilder;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
@@ -209,13 +210,14 @@
         LIR lir = gen.getLIR();
         assert gen.getDeoptimizationRescueSlot() == null || frameMap.frameNeedsAllocating() : "method that can deoptimize must have a frame";
         OptionValues options = lir.getOptions();
+        DebugContext debug = lir.getDebug();
         boolean omitFrame = CanOmitFrame.getValue(options) && !frameMap.frameNeedsAllocating() && !lir.hasArgInCallerFrame() && !gen.hasForeignCall();
 
         Stub stub = gen.getStub();
         Assembler masm = createAssembler(frameMap);
         HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null, omitFrame);
         DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget());
-        CompilationResultBuilder crb = factory.createBuilder(getCodeCache(), getForeignCalls(), frameMap, masm, dataBuilder, frameContext, options, compilationResult);
+        CompilationResultBuilder crb = factory.createBuilder(getCodeCache(), getForeignCalls(), frameMap, masm, dataBuilder, frameContext, options, debug, compilationResult);
         crb.setTotalFrameSize(frameMap.totalFrameSize());
         crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize());
         StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -41,7 +41,7 @@
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
 import org.graalvm.compiler.core.common.spi.LIRKindTool;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotBackend;
@@ -555,7 +555,7 @@
             LIR lir = getResult().getLIR();
             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(lir.getControlFlowGraph().getStartBlock());
             instructions.add(1, op);
-            Debug.dump(Debug.INFO_LEVEL, lir, "created rescue dummy op");
+            lir.getDebug().dump(DebugContext.INFO_LEVEL, lir, "created rescue dummy op");
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,6 +33,7 @@
 
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
@@ -63,11 +64,11 @@
     }
 
     @Override
-    public void initialize(OptionValues options, HotSpotProviders providers, GraalHotSpotVMConfig config) {
-        convertSnippets = new AMD64ConvertSnippets.Templates(options, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
+    public void initialize(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, GraalHotSpotVMConfig config) {
+        convertSnippets = new AMD64ConvertSnippets.Templates(options, factories, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget());
         profileSnippets = ProfileNode.Options.ProbabilisticProfiling.getValue(options)
-                        ? new ProbabilisticProfileSnippets.Templates(options, providers, providers.getCodeCache().getTarget()) : null;
-        super.initialize(options, providers, config);
+                        ? new ProbabilisticProfileSnippets.Templates(options, factories, providers, providers.getCodeCache().getTarget()) : null;
+        super.initialize(options, factories, providers, config);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,15 @@
  */
 package org.graalvm.compiler.hotspot.amd64;
 
-import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 import static jdk.vm.ci.amd64.AMD64.rbp;
 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 
 import org.graalvm.compiler.core.amd64.AMD64NodeLIRBuilder;
 import org.graalvm.compiler.core.amd64.AMD64NodeMatchRules;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
 import org.graalvm.compiler.core.gen.DebugInfoBuilder;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder;
 import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
 import org.graalvm.compiler.hotspot.HotSpotLockStack;
@@ -177,7 +176,7 @@
     @Override
     public void visitFullInfopointNode(FullInfopointNode i) {
         if (i.getState() != null && i.getState().bci == BytecodeFrame.AFTER_BCI) {
-            Debug.log("Ignoring InfopointNode for AFTER_BCI");
+            i.getDebug().log("Ignoring InfopointNode for AFTER_BCI");
         } else {
             super.visitFullInfopointNode(i);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -59,8 +59,8 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.sparc.SPARCNodeMatchRules;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotDataBuilder;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
@@ -117,7 +117,7 @@
     }
 
     private static class SizeEstimateStatistics {
-        private static final ConcurrentHashMap<String, DebugCounter> counters = new ConcurrentHashMap<>();
+        private static final ConcurrentHashMap<String, CounterKey> counters = new ConcurrentHashMap<>();
         private final String suffix;
 
         SizeEstimateStatistics(String suffix) {
@@ -125,10 +125,10 @@
             this.suffix = suffix;
         }
 
-        public void add(Class<?> c, int count) {
+        public void add(Class<?> c, int count, DebugContext debug) {
             String name = SizeEstimateStatistics.class.getSimpleName() + "_" + c.getSimpleName() + "." + suffix;
-            DebugCounter m = counters.computeIfAbsent(name, (n) -> Debug.counter(n));
-            m.add(count);
+            CounterKey m = counters.computeIfAbsent(name, (n) -> DebugContext.counter(n));
+            m.add(debug, count);
         }
     }
 
@@ -239,7 +239,9 @@
         HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null);
         DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget());
         OptionValues options = lir.getOptions();
-        CompilationResultBuilder crb = factory.createBuilder(getProviders().getCodeCache(), getProviders().getForeignCalls(), frameMap, masm, dataBuilder, frameContext, options, compilationResult);
+        DebugContext debug = lir.getDebug();
+        CompilationResultBuilder crb = factory.createBuilder(getProviders().getCodeCache(), getProviders().getForeignCalls(), frameMap, masm, dataBuilder, frameContext, options, debug,
+                        compilationResult);
         crb.setTotalFrameSize(frameMap.totalFrameSize());
         crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize());
         StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot();
@@ -253,7 +255,7 @@
             EconomicMap<LIRFrameState, SaveRegistersOp> calleeSaveInfo = gen.getCalleeSaveInfo();
             updateStub(stub, destroyedCallerRegisters, calleeSaveInfo, frameMap);
         }
-        assert registerSizePredictionValidator(crb);
+        assert registerSizePredictionValidator(crb, debug);
         return crb;
     }
 
@@ -261,14 +263,19 @@
      * Registers a verifier which checks if the LIRInstructions estimate of constants size is
      * greater or equal to the actual one.
      */
-    private static boolean registerSizePredictionValidator(final CompilationResultBuilder crb) {
+    private static boolean registerSizePredictionValidator(final CompilationResultBuilder crb, DebugContext debug) {
         /**
          * Used to hold state between beforeOp and afterOp
          */
         class ValidationState {
             LIRInstruction op;
+            final DebugContext debug;
             int constantSizeBefore;
 
+            ValidationState(DebugContext debug) {
+                this.debug = debug;
+            }
+
             public void before(LIRInstruction before) {
                 assert op == null : "LIRInstruction " + op + " no after call received";
                 op = before;
@@ -283,8 +290,8 @@
                     org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin.SizeEstimate size = ((SPARCLIRInstructionMixin) op).estimateSize();
                     assert size != null : "No size prediction available for op: " + op;
                     Class<?> c = op.getClass();
-                    CONSTANT_ESTIMATED_STATS.add(c, size.constantSize);
-                    CONSTANT_ACTUAL_STATS.add(c, actual);
+                    CONSTANT_ESTIMATED_STATS.add(c, size.constantSize, debug);
+                    CONSTANT_ACTUAL_STATS.add(c, actual, debug);
                     assert size.constantSize >= actual : "Op " + op + " exceeded estimated constant size; predicted: " + size.constantSize + " actual: " + actual;
                 } else {
                     assert actual == 0 : "Op " + op + " emitted to DataSection without any estimate.";
@@ -293,7 +300,7 @@
                 constantSizeBefore = 0;
             }
         }
-        final ValidationState state = new ValidationState();
+        final ValidationState state = new ValidationState(debug);
         crb.setOpCallback(op -> state.before(op), op -> state.after(op));
         return true;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,15 @@
  */
 package org.graalvm.compiler.hotspot.sparc;
 
-import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 import static jdk.vm.ci.sparc.SPARC.g5;
 import static jdk.vm.ci.sparc.SPARC.o7;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER_IN_CALLER;
 
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
 import org.graalvm.compiler.core.gen.DebugInfoBuilder;
 import org.graalvm.compiler.core.sparc.SPARCNodeLIRBuilder;
 import org.graalvm.compiler.core.sparc.SPARCNodeMatchRules;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder;
 import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
 import org.graalvm.compiler.hotspot.HotSpotLockStack;
@@ -150,7 +149,7 @@
     @Override
     public void visitFullInfopointNode(FullInfopointNode i) {
         if (i.getState() != null && i.getState().bci == BytecodeFrame.AFTER_BCI) {
-            Debug.log("Ignoring InfopointNode for AFTER_BCI");
+            i.getDebug().log("Ignoring InfopointNode for AFTER_BCI");
         } else {
             super.visitFullInfopointNode(i);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ClassSubstitutionsTests.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ClassSubstitutionsTests.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,16 +23,14 @@
 
 package org.graalvm.compiler.hotspot.test;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.junit.Test;
 
 public class ClassSubstitutionsTests extends GraalCompilerTest {
 
@@ -44,14 +42,15 @@
 
     @SuppressWarnings("try")
     protected StructuredGraph test(final String snippet) {
-        try (Scope s = Debug.scope("ClassSubstitutionsTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("ClassSubstitutionsTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             compile(graph.method(), graph);
             assertNotInGraph(graph, Invoke.class);
-            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
+            debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(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/CompileTheWorld.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintBailout;
 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintStackTraceOnException;
 import static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries;
+import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
 import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
 import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
 
@@ -68,14 +69,11 @@
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.bytecode.Bytecodes;
 import org.graalvm.compiler.core.CompilerThreadFactory;
-import org.graalvm.compiler.core.CompilerThreadFactory.DebugConfigAccess;
 import org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
-import org.graalvm.compiler.debug.DebugEnvironment;
-import org.graalvm.compiler.debug.GraalDebugConfig;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.MethodFilter;
 import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.MemUseTrackerImpl;
 import org.graalvm.compiler.hotspot.CompilationTask;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
@@ -219,7 +217,7 @@
         PrintStackTraceOnException.putIfAbsent(compilationOptionsCopy, true);
 
         // By default only report statistics for the CTW threads themselves
-        GraalDebugConfig.Options.DebugValueThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
+        DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
         this.compilationOptions = compilationOptionsCopy;
     }
 
@@ -511,13 +509,7 @@
 
         OptionValues savedOptions = currentOptions;
         currentOptions = new OptionValues(compilationOptions);
-        threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
-                        new CompilerThreadFactory("CompileTheWorld", new DebugConfigAccess() {
-                            @Override
-                            public GraalDebugConfig getDebugConfig() {
-                                return DebugEnvironment.ensureInitialized(currentOptions, compiler.getGraalRuntime().getHostProviders().getSnippetReflection());
-                            }
-                        }));
+        threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
 
         try {
             for (int i = 0; i < entries.length; i++) {
@@ -703,7 +695,7 @@
     private void compileMethod(HotSpotResolvedJavaMethod method, int counter) {
         try {
             long start = System.currentTimeMillis();
-            long allocatedAtStart = MemUseTrackerImpl.getCurrentThreadAllocatedBytes();
+            long allocatedAtStart = getCurrentThreadAllocatedBytes();
             int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
             HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
             // For more stable CTW execution, disable use of profiling information
@@ -718,7 +710,7 @@
                 installedCode.invalidate();
             }
 
-            memoryUsed.getAndAdd(MemUseTrackerImpl.getCurrentThreadAllocatedBytes() - allocatedAtStart);
+            memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart);
             compileTime.getAndAdd(System.currentTimeMillis() - start);
             compiledMethodsCounter.incrementAndGet();
         } catch (Throwable t) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,8 +28,7 @@
 import java.lang.reflect.Method;
 
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -52,14 +51,15 @@
     @SuppressWarnings("try")
     protected StructuredGraph test(final String snippet) {
         ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(snippet));
-        try (Scope s = Debug.scope("ConstantPoolSubstitutionsTests", method)) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("ConstantPoolSubstitutionsTests", method)) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             compile(graph.method(), graph);
             assertNotInGraph(graph, Invoke.class);
-            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
+            debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(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/GraalOSRLockTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRLockTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,7 +32,7 @@
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.core.phases.HighTier;
-import org.graalvm.compiler.debug.DebugEnvironment;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.hotspot.phases.OnStackReplacementPhase;
@@ -40,7 +40,6 @@
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.util.EconomicMap;
 import org.junit.Assert;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -149,11 +148,6 @@
         System.gc();
     }
 
-    @BeforeClass
-    public static void init() {
-        DebugEnvironment.ensureInitialized(getInitialOptions());
-    }
-
     // @Test
     @SuppressWarnings("try")
     public void testLockOSROuterImmediateDeoptAfter() {
@@ -313,7 +307,8 @@
             EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt();
             overrides.put(HighTier.Options.Inline, false);
             OptionValues options = new OptionValues(getInitialOptions(), overrides);
-            compile(options, leaf, -1);
+            DebugContext debug = getDebugContext(options);
+            compile(debug, leaf, -1);
             testOSR(options, "testRecursiveLockingRoot");
         });
     }
@@ -327,7 +322,8 @@
             EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt();
             overrides.put(HighTier.Options.Inline, false);
             OptionValues options = new OptionValues(getInitialOptions(), overrides);
-            compile(options, root, -1);
+            DebugContext debug = getDebugContext(options);
+            compile(debug, root, -1);
             testOSR(options, "testRecursiveLeafOSR");
             // force a safepoint and hope the inflated locks are deflated
             System.gc();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,12 +31,14 @@
 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
 import org.graalvm.compiler.core.target.Backend;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.hotspot.CompilationTask;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
 import org.graalvm.compiler.java.BciBlockMapping;
 import org.graalvm.compiler.java.BciBlockMapping.BciBlock;
+import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.options.OptionValues;
 import org.junit.Assert;
@@ -68,13 +70,13 @@
         checkResult(result);
     }
 
-    protected static void compile(OptionValues options, ResolvedJavaMethod method, int bci) {
+    protected static void compile(DebugContext debug, ResolvedJavaMethod method, int bci) {
         HotSpotJVMCIRuntimeProvider runtime = HotSpotJVMCIRuntime.runtime();
         long jvmciEnv = 0L;
         HotSpotCompilationRequest request = new HotSpotCompilationRequest((HotSpotResolvedJavaMethod) method, bci, jvmciEnv);
         HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) runtime.getCompiler();
-        CompilationTask task = new CompilationTask(runtime, compiler, request, true, true, options);
-        HotSpotCompilationRequestResult result = task.runCompilation();
+        CompilationTask task = new CompilationTask(runtime, compiler, request, true, true, debug.getOptions());
+        HotSpotCompilationRequestResult result = task.runCompilation(debug);
         if (result.getFailure() != null) {
             throw new GraalError(result.getFailureMessage());
         }
@@ -84,10 +86,11 @@
      * Returns the target BCI of the first bytecode backedge. This is where HotSpot triggers
      * on-stack-replacement in case the backedge counter overflows.
      */
-    private static int getBackedgeBCI(ResolvedJavaMethod method) {
+    private static int getBackedgeBCI(DebugContext debug, ResolvedJavaMethod method) {
         Bytecode code = new ResolvedJavaMethodBytecode(method);
         BytecodeStream stream = new BytecodeStream(code.getCode());
-        BciBlockMapping bciBlockMapping = BciBlockMapping.create(stream, code, getInitialOptions());
+        OptionValues options = debug.getOptions();
+        BciBlockMapping bciBlockMapping = BciBlockMapping.create(stream, code, options, debug);
 
         for (BciBlock block : bciBlockMapping.getBlocks()) {
             if (block.startBci != -1) {
@@ -117,12 +120,11 @@
 
     private void compileOSR(OptionValues options, ResolvedJavaMethod method) {
         // ensure eager resolving
-        parseEager(method, AllowAssumptions.YES, options);
-        int bci = getBackedgeBCI(method);
+        StructuredGraph graph = parseEager(method, AllowAssumptions.YES, options);
+        DebugContext debug = graph.getDebug();
+        int bci = getBackedgeBCI(debug, method);
         assert bci != -1;
-        // ensure eager resolving
-        parseEager(method, AllowAssumptions.YES, options);
-        compile(options, method, bci);
+        compile(debug, method, bci);
     }
 
     protected enum ReturnValue {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotCryptoSubstitutionTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotCryptoSubstitutionTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -37,6 +37,7 @@
 import org.junit.Test;
 
 import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.meta.HotSpotGraphBuilderPlugins;
 
 import jdk.vm.ci.code.InstalledCode;
@@ -48,8 +49,8 @@
 public class HotSpotCryptoSubstitutionTest extends HotSpotGraalCompilerTest {
 
     @Override
-    protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult) {
-        return getBackend().createDefaultInstalledCode(method, compResult);
+    protected InstalledCode addMethod(DebugContext debug, ResolvedJavaMethod method, CompilationResult compResult) {
+        return getBackend().createDefaultInstalledCode(debug, method, compResult);
     }
 
     SecretKey aesKey;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalCompilerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalCompilerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,6 +30,7 @@
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 
 import jdk.vm.ci.code.InstalledCode;
@@ -54,7 +55,8 @@
         HotSpotGraalRuntimeProvider rt = (HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class);
         HotSpotProviders providers = rt.getHostBackend().getProviders();
         CompilationIdentifier compilationId = runtime().getHostBackend().getCompilationIdentifier(method);
-        StructuredGraph graph = compiler.getIntrinsicGraph(method, providers, compilationId, getInitialOptions());
+        OptionValues options = getInitialOptions();
+        StructuredGraph graph = compiler.getIntrinsicGraph(method, providers, compilationId, options, getDebugContext(options));
         if (graph != null) {
             return getCode(method, graph, true, true, graph.getOptions());
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -41,7 +41,7 @@
 import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 
-import org.graalvm.compiler.debug.GraalDebugConfig;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.hotspot.HotSpotGraalMBean;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.test.GraalTest;
@@ -227,14 +227,14 @@
         ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Arrays.class.getMethod("asList", Object[].class));
         final OptionValues forMethod = realBean.optionsFor(unsetDump, method);
         assertNotSame(unsetDump, forMethod);
-        Object nothing = unsetDump.getMap().get(GraalDebugConfig.Options.Dump);
+        Object nothing = unsetDump.getMap().get(DebugOptions.Dump);
         assertEquals("Empty string", "", nothing);
 
-        Object specialValue = forMethod.getMap().get(GraalDebugConfig.Options.Dump);
+        Object specialValue = forMethod.getMap().get(DebugOptions.Dump);
         assertEquals(":3", specialValue);
 
         OptionValues normalMethod = realBean.optionsFor(unsetDump, null);
-        Object noSpecialValue = normalMethod.getMap().get(GraalDebugConfig.Options.Dump);
+        Object noSpecialValue = normalMethod.getMap().get(DebugOptions.Dump);
         assertEquals("Empty string", "", noSpecialValue);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMonitorValueTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMonitorValueTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,6 +34,7 @@
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 
 import jdk.vm.ci.code.BytecodeFrame;
@@ -46,7 +47,7 @@
 public class HotSpotMonitorValueTest extends GraalCompilerTest {
 
     @Override
-    protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult) {
+    protected InstalledCode addMethod(DebugContext debug, ResolvedJavaMethod method, CompilationResult compResult) {
         for (Infopoint i : compResult.getInfopoints()) {
             if (i instanceof Call) {
                 Call call = (Call) i;
@@ -75,7 +76,7 @@
                         }
                         assertDeepEquals(lock3.getOwner(), lock4.getOwner());
                         assertThat(lock1.getOwner(), not(lock2.getOwner()));
-                        return super.addMethod(method, compResult);
+                        return super.addMethod(debug, method, compResult);
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotNodeSubstitutionsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotNodeSubstitutionsTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,11 +24,13 @@
 
 import org.junit.Test;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.replacements.test.MethodSubstitutionTest;
 
 /**
@@ -38,7 +40,9 @@
 
     @Test
     public void test() {
-        StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).build();
+        OptionValues options = getInitialOptions();
+        DebugContext debug = getDebugContext(options);
+        StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).build();
         test("getNodeClass", ConstantNode.forInt(42, graph));
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/InstalledCodeExecuteHelperTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/InstalledCodeExecuteHelperTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,13 +24,13 @@
 
 import static java.lang.reflect.Modifier.isStatic;
 
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -76,9 +76,10 @@
     }
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        StructuredGraph graph = super.parseEager(m, allowAssumptions, compilationId, options);
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        StructuredGraph graph = super.parse(builder, graphBuilderSuite);
         if (argsToBind != null) {
+            ResolvedJavaMethod m = graph.method();
             Object receiver = isStatic(m.getModifiers()) ? null : this;
             Object[] args = argsWithReceiver(receiver, argsToBind);
             JavaType[] parameterTypes = m.toParameterTypes();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/JVMCIInfopointErrorTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/JVMCIInfopointErrorTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,14 +29,13 @@
 
 import java.util.function.Consumer;
 
-import org.junit.Test;
-
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
 import org.graalvm.compiler.lir.FullInfopointOp;
@@ -51,6 +50,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
+import org.junit.Test;
 
 import jdk.vm.ci.code.BytecodeFrame;
 import jdk.vm.ci.code.CodeCacheProvider;
@@ -134,9 +134,13 @@
     }
 
     private void test(TestSpec spec) {
+        test(getDebugContext(), spec);
+    }
+
+    private void test(DebugContext debug, TestSpec spec) {
         ResolvedJavaMethod method = getResolvedJavaMethod("testMethod");
 
-        StructuredGraph graph = parseForCompile(method);
+        StructuredGraph graph = parseForCompile(method, debug);
         TestNode test = graph.add(new TestNode(spec));
         graph.addAfterFixed(graph.start(), test);
 
@@ -266,12 +270,13 @@
     @SuppressWarnings("try")
     @Test(expected = Error.class)
     public void testUnknownJavaValue() {
-        try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) {
+        DebugContext debug = DebugContext.create(getInitialOptions(), DebugHandlersFactory.LOADER);
+        try (Scope s = debug.disable()) {
             /*
              * Expected: either AssertionError or GraalError, depending on whether the unit test run
              * is with assertions enabled or disabled.
              */
-            test((tool, state, safepoint) -> {
+            test(debug, (tool, state, safepoint) -> {
                 LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{new UnknownJavaValue()}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
                 safepoint.accept(newState);
             });
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/MemoryUsageBenchmark.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/MemoryUsageBenchmark.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,10 @@
  */
 package org.graalvm.compiler.hotspot.test;
 
-import static org.graalvm.compiler.debug.internal.MemUseTrackerImpl.getCurrentThreadAllocatedBytes;
+import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
 
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.core.test.AllocSpy;
-import org.graalvm.compiler.debug.DebugEnvironment;
 import org.graalvm.compiler.hotspot.CompilationTask;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -121,8 +120,6 @@
         // may include processing command line options used by the latter.
         Graal.getRuntime();
 
-        // Ensure a debug configuration for this thread is initialized
-        DebugEnvironment.ensureInitialized(getInitialOptions());
         new MemoryUsageBenchmark().run();
     }
 
@@ -156,7 +153,9 @@
             try (AllocSpy as = AllocSpy.open(methodName)) {
                 HotSpotJVMCIRuntimeProvider runtime = HotSpotJVMCIRuntime.runtime();
                 HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, JVMCICompiler.INVOCATION_ENTRY_BCI, jvmciEnv);
-                CompilationTask task = new CompilationTask(runtime, (HotSpotGraalCompiler) runtime.getCompiler(), request, true, false, getInitialOptions());
+                HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) runtime.getCompiler();
+                OptionValues options = getInitialOptions();
+                CompilationTask task = new CompilationTask(runtime, compiler, request, true, false, options);
                 task.runCompilation();
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,9 +22,9 @@
  */
 package org.graalvm.compiler.hotspot.test;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Dump;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.MethodFilter;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintGraph;
+import static org.graalvm.compiler.debug.DebugOptions.Dump;
+import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
+import static org.graalvm.compiler.debug.DebugOptions.PrintGraph;
 import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
 import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -111,7 +111,7 @@
                 }
             }
             if (bgv == 0) {
-                Assert.fail(String.format("Expected at least one .bgv file in %s: %s", diagnosticOutputZip, entries));
+                Assert.fail(String.format("Expected at least one .bgv file in %s: %s%n%s", diagnosticOutputZip, entries, proc));
             }
             if (cfg == 0) {
                 Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/TestIntrinsicCompiles.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/TestIntrinsicCompiles.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,11 +23,11 @@
 package org.graalvm.compiler.hotspot.test;
 
 import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID;
-
 import java.util.List;
 
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -36,6 +36,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Binding;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.graalvm.util.EconomicMap;
@@ -63,13 +64,15 @@
         EconomicMap<String, List<Binding>> bindings = invocationPlugins.getBindings(true);
         HotSpotVMConfigStore store = rt.getVMConfig().getStore();
         List<VMIntrinsicMethod> intrinsics = store.getIntrinsics();
+        OptionValues options = getInitialOptions();
+        DebugContext debug = getDebugContext(options);
         for (VMIntrinsicMethod intrinsic : intrinsics) {
             InvocationPlugin plugin = CheckGraalIntrinsics.findPlugin(bindings, intrinsic);
             if (plugin != null) {
                 if (plugin instanceof MethodSubstitutionPlugin) {
                     ResolvedJavaMethod method = CheckGraalIntrinsics.resolveIntrinsic(getMetaAccess(), intrinsic);
                     if (!method.isNative()) {
-                        StructuredGraph graph = compiler.getIntrinsicGraph(method, providers, INVALID_COMPILATION_ID, getInitialOptions());
+                        StructuredGraph graph = compiler.getIntrinsicGraph(method, providers, INVALID_COMPILATION_ID, options, debug);
                         getCode(method, graph);
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,11 +27,7 @@
 
 import java.lang.ref.WeakReference;
 
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.nodes.G1PostWriteBarrier;
 import org.graalvm.compiler.hotspot.nodes.G1PreWriteBarrier;
@@ -53,6 +49,8 @@
 import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
+import org.junit.Assert;
+import org.junit.Test;
 
 import jdk.vm.ci.hotspot.HotSpotInstalledCode;
 import jdk.vm.ci.meta.JavaConstant;
@@ -256,8 +254,9 @@
     @SuppressWarnings("try")
     private void testHelper(final String snippetName, final int expectedBarriers) throws Exception, SecurityException {
         ResolvedJavaMethod snippet = getResolvedJavaMethod(snippetName);
-        try (Scope s = Debug.scope("WriteBarrierAdditionTest", snippet)) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("WriteBarrierAdditionTest", snippet)) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
             HighTierContext highContext = getDefaultHighTierContext();
             MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
             new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
@@ -266,7 +265,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_LEVEL, graph, "After Write Barrier Addition");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "After Write Barrier Addition");
 
             int barriers = 0;
             if (config.useG1GC) {
@@ -305,7 +304,7 @@
                 }
             }
         } catch (Throwable e) {
-            throw Debug.handle(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/WriteBarrierVerificationTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,14 +23,11 @@
 package org.graalvm.compiler.hotspot.test;
 
 import java.util.List;
-import org.junit.Assert;
-import org.junit.Test;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugConfigScope;
+
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.debug.DebugDumpScope;
-import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.nodes.G1ArrayRangePostWriteBarrier;
 import org.graalvm.compiler.hotspot.nodes.G1ArrayRangePreWriteBarrier;
@@ -64,6 +61,8 @@
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.word.LocationIdentity;
+import org.junit.Assert;
+import org.junit.Test;
 
 import jdk.vm.ci.meta.ResolvedJavaField;
 
@@ -647,8 +646,9 @@
 
     @SuppressWarnings("try")
     private void testPredicate(final String snippet, final GraphPredicate expectedBarriers, final int... removedBarrierIndices) {
-        try (Scope d = Debug.scope("WriteBarrierVerificationTest", new DebugDumpScope(snippet))) {
-            final StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugCloseable d = debug.disableIntercept(); DebugContext.Scope s = debug.scope("WriteBarrierVerificationTest", new DebugDumpScope(snippet))) {
+            final StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             HighTierContext highTierContext = getDefaultHighTierContext();
             new InliningPhase(new CanonicalizerPhase()).apply(graph, highTierContext);
 
@@ -725,10 +725,7 @@
                 }
             };
 
-            DebugConfig debugConfig = DebugScope.getConfig();
-            DebugConfig fixedConfig = debugConfig == null ? null
-                            : Debug.fixedConfig(debugConfig.getOptions(), 0, 0, false, false, false, false, false, debugConfig.dumpHandlers(), debugConfig.verifyHandlers(), debugConfig.output());
-            try (DebugConfigScope s = Debug.setConfig(fixedConfig)) {
+            try (Scope disabled = debug.disable()) {
                 ReentrantNodeIterator.apply(closure, graph.start(), false);
                 new WriteBarrierVerificationPhase(config).apply(graph);
             } catch (AssertionError error) {
@@ -740,7 +737,7 @@
                 throw error;
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,19 +34,21 @@
 
 import java.util.List;
 
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.code.CompilationResult;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpScope;
-import org.graalvm.compiler.debug.DebugTimer;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Management;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.debug.TimeSource;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 import org.graalvm.util.EconomicMap;
 
 import jdk.vm.ci.code.BailoutException;
@@ -63,7 +65,7 @@
 
 public class CompilationTask {
 
-    private static final DebugCounter BAILOUTS = Debug.counter("Bailouts");
+    private static final CounterKey BAILOUTS = DebugContext.counter("Bailouts");
 
     private static final EventProvider eventProvider;
 
@@ -99,7 +101,7 @@
         CompilationResult result;
 
         RetryableCompilation(EventProvider.CompilationEvent compilationEvent) {
-            super(compiler.getGraalRuntime(), options);
+            super(compiler.getGraalRuntime());
             this.compilationEvent = compilationEvent;
         }
 
@@ -110,7 +112,7 @@
 
         @SuppressWarnings("try")
         @Override
-        protected HotSpotCompilationRequestResult run(Throwable retryCause) {
+        protected HotSpotCompilationRequestResult run(DebugContext debug, Throwable retryCause) {
             HotSpotResolvedJavaMethod method = getMethod();
             int entryBCI = getEntryBCI();
             final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
@@ -133,15 +135,15 @@
                 allocatedBytesBefore = 0L;
             }
 
-            try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
+            try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
                 // Begin the compilation event.
                 compilationEvent.begin();
-                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options);
+                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options, debug);
             } catch (Throwable e) {
                 if (retryCause != null) {
                     log("Exception during retry", e);
                 }
-                throw Debug.handle(e);
+                throw debug.handle(e);
             } finally {
                 // End the compilation event.
                 compilationEvent.end();
@@ -166,8 +168,8 @@
             }
 
             if (result != null) {
-                try (DebugCloseable b = CodeInstallationTime.start()) {
-                    installMethod(result);
+                try (DebugCloseable b = CodeInstallationTime.start(debug)) {
+                    installMethod(debug, result);
                 }
             }
             stats.finish(method, installedCode);
@@ -219,10 +221,14 @@
         return getRequest().getMethod();
     }
 
+    CompilationIdentifier getCompilationIdentifier() {
+        return compilationId;
+    }
+
     /**
-     * Returns the compilation id of this task.
+     * Returns the HostSpot id of this compilation.
      *
-     * @return compile id
+     * @return HotSpot compile id
      */
     public int getId() {
         return getRequest().getId();
@@ -251,46 +257,44 @@
     /**
      * Time spent in compilation.
      */
-    private static final DebugTimer CompilationTime = Debug.timer("CompilationTime");
+    private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation.");
 
     /**
      * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}.
      */
-    private static final DebugCounter CompiledBytecodes = Debug.counter("CompiledBytecodes");
+    private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes");
 
     /**
      * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for
      * which {@linkplain CompilationResult#getTargetCode()} code was installed.
      */
-    private static final DebugCounter CompiledAndInstalledBytecodes = Debug.counter("CompiledAndInstalledBytecodes");
+    private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes");
 
     /**
      * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes.
      */
-    private static final DebugCounter InstalledCodeSize = Debug.counter("InstalledCodeSize");
+    private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize");
 
     /**
      * Time spent in code installation.
      */
-    public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation");
+    public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation");
+
+    public HotSpotCompilationRequestResult runCompilation() {
+        SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
+        try (DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(snippetReflection))) {
+            return runCompilation(debug);
+        }
+    }
 
     @SuppressWarnings("try")
-    public HotSpotCompilationRequestResult runCompilation() {
+    public HotSpotCompilationRequestResult runCompilation(DebugContext debug) {
         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
         int entryBCI = getEntryBCI();
         boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
         HotSpotResolvedJavaMethod method = getMethod();
 
-        // register the compilation id in the method metrics
-        if (Debug.isMethodMeterEnabled()) {
-            if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
-                Debug.methodMetrics(method).addToMetric(getId(), "CompilationIdOSR");
-            } else {
-                Debug.methodMetrics(method).addToMetric(getId(), "CompilationId");
-            }
-        }
-
         // Log a compilation event.
         EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
 
@@ -304,10 +308,10 @@
         }
 
         RetryableCompilation compilation = new RetryableCompilation(compilationEvent);
-        try (DebugCloseable a = CompilationTime.start()) {
-            return compilation.execute();
+        try (DebugCloseable a = CompilationTime.start(debug)) {
+            return compilation.runWithRetry(debug);
         } catch (BailoutException bailout) {
-            BAILOUTS.increment();
+            BAILOUTS.increment(debug);
             if (ExitVMOnBailout.getValue(options)) {
                 TTY.out.println(method.format("Bailout in %H.%n(%p)"));
                 bailout.printStackTrace(TTY.out);
@@ -350,11 +354,11 @@
 
                 if (compilation.result != null) {
                     compiledBytecodes = compilation.result.getBytecodeSize();
-                    CompiledBytecodes.add(compiledBytecodes);
+                    CompiledBytecodes.add(debug, compiledBytecodes);
                     if (installedCode != null) {
                         codeSize = installedCode.getSize();
-                        CompiledAndInstalledBytecodes.add(compiledBytecodes);
-                        InstalledCodeSize.add(codeSize);
+                        CompiledAndInstalledBytecodes.add(debug, compiledBytecodes);
+                        InstalledCodeSize.add(debug, codeSize);
                     }
                 }
 
@@ -411,13 +415,17 @@
     }
 
     @SuppressWarnings("try")
-    private void installMethod(final CompilationResult compResult) {
+    private void installMethod(DebugContext debug, final CompilationResult compResult) {
         final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache();
         HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend();
         installedCode = null;
         Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
-        installedCode = (HotSpotInstalledCode) backend.createInstalledCode(getRequest().getMethod(), getRequest(), compResult,
-                        getRequest().getMethod().getSpeculationLog(), null, installAsDefault, context);
+        try (DebugContext.Scope s = debug.scope("CodeInstall", context)) {
+            installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, getRequest().getMethod(), getRequest(), compResult,
+                            getRequest().getMethod().getSpeculationLog(), null, installAsDefault, context);
+        } catch (Throwable e) {
+            throw debug.handle(e);
+        }
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/FingerprintUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 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;
-
-import java.lang.reflect.Method;
-
-import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
-
-/**
- * Reflective wrapper around the fingerprint generation functionality in JDK9.
- */
-public class FingerprintUtil {
-    private static Method getFingerprint;
-    static {
-        try {
-            getFingerprint = HotSpotResolvedObjectType.class.getMethod("getFingerprint");
-        } catch (Exception e) {
-        }
-    }
-
-    public static long getFingerprint(HotSpotResolvedObjectType type) {
-        if (getFingerprint != null) {
-            try {
-                return ((Long) getFingerprint.invoke(type)).longValue();
-            } catch (Exception e) {
-            }
-        }
-        return 0;
-    }
-}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -57,7 +57,7 @@
     private HotSpotCodeCacheProvider codeCacheProvider;
 
     public HotSpotDebugInfoBuilder(NodeValueMap nodeValueMap, HotSpotLockStack lockStack, HotSpotLIRGenerator gen) {
-        super(nodeValueMap);
+        super(nodeValueMap, gen.getResult().getLIR().getDebug());
         this.lockStack = lockStack;
         this.codeCacheProvider = gen.getProviders().getCodeCache();
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,10 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.util.Collections;
 import java.util.Formattable;
 import java.util.Formatter;
+import java.util.List;
 
 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler;
 import org.graalvm.compiler.bytecode.Bytecode;
@@ -36,12 +38,10 @@
 import org.graalvm.compiler.core.GraalCompiler;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.util.CompilationAlarm;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
-import org.graalvm.compiler.debug.DebugEnvironment;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.TopLevelDebugConfig;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsRootScopeInfo;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Activation;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.hotspot.CompilationCounters.Options;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.phases.OnStackReplacementPhase;
@@ -60,6 +60,7 @@
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.Suites;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 
 import jdk.vm.ci.code.CompilationRequest;
 import jdk.vm.ci.code.CompilationRequestResult;
@@ -80,13 +81,21 @@
     private final HotSpotGraalRuntimeProvider graalRuntime;
     private final CompilationCounters compilationCounters;
     private final BootstrapWatchDog bootstrapWatchDog;
+    private List<DebugHandlersFactory> factories;
 
     HotSpotGraalCompiler(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalRuntimeProvider graalRuntime, OptionValues options) {
         this.jvmciRuntime = jvmciRuntime;
         this.graalRuntime = graalRuntime;
         // It is sufficient to have one compilation counter object per Graal compiler object.
         this.compilationCounters = Options.CompilationCountLimit.getValue(options) > 0 ? new CompilationCounters(options) : null;
-        this.bootstrapWatchDog = graalRuntime.isBootstrapping() && !GraalDebugConfig.Options.BootstrapInitializeOnly.getValue(options) ? BootstrapWatchDog.maybeCreate(graalRuntime) : null;
+        this.bootstrapWatchDog = graalRuntime.isBootstrapping() && !DebugOptions.BootstrapInitializeOnly.getValue(options) ? BootstrapWatchDog.maybeCreate(graalRuntime) : null;
+    }
+
+    public List<DebugHandlersFactory> getDebugHandlersFactories() {
+        if (factories == null) {
+            factories = Collections.singletonList(new GraalDebugHandlersFactory(graalRuntime.getHostProviders().getSnippetReflection()));
+        }
+        return factories;
     }
 
     @Override
@@ -104,11 +113,13 @@
         if (graalRuntime.isShutdown()) {
             return HotSpotCompilationRequestResult.failure(String.format("Shutdown entered"), false);
         }
-        OptionValues options = graalRuntime.getOptions(request.getMethod());
+
+        ResolvedJavaMethod method = request.getMethod();
+        OptionValues options = graalRuntime.getOptions(method);
 
         if (graalRuntime.isBootstrapping()) {
-            if (GraalDebugConfig.Options.BootstrapInitializeOnly.getValue(options)) {
-                return HotSpotCompilationRequestResult.failure(String.format("Skip compilation because %s is enabled", GraalDebugConfig.Options.BootstrapInitializeOnly.getName()), true);
+            if (DebugOptions.BootstrapInitializeOnly.getValue(options)) {
+                return HotSpotCompilationRequestResult.failure(String.format("Skip compilation because %s is enabled", DebugOptions.BootstrapInitializeOnly.getName()), true);
             }
             if (bootstrapWatchDog != null) {
                 if (bootstrapWatchDog.hitCriticalCompilationRateOrTimeout()) {
@@ -117,7 +128,6 @@
                 }
             }
         }
-        ResolvedJavaMethod method = request.getMethod();
         HotSpotCompilationRequest hsRequest = (HotSpotCompilationRequest) request;
         try (CompilationWatchDog w1 = CompilationWatchDog.watch(method, hsRequest.getId(), options);
                         BootstrapWatchDog.Watch w2 = bootstrapWatchDog == null ? null : bootstrapWatchDog.watch(request);
@@ -125,31 +135,29 @@
             if (compilationCounters != null) {
                 compilationCounters.countCompilation(method);
             }
-            // Ensure a debug configuration for this thread is initialized
-            DebugEnvironment.ensureInitialized(options, graalRuntime.getHostProviders().getSnippetReflection());
             CompilationTask task = new CompilationTask(jvmciRuntime, this, hsRequest, true, installAsDefault, options);
-            CompilationRequestResult r;
-            try (DebugConfigScope dcs = Debug.setConfig(new TopLevelDebugConfig());
-                            Debug.Scope s = Debug.methodMetricsScope("HotSpotGraalCompiler", MethodMetricsRootScopeInfo.create(method), true, method)) {
-                r = task.runCompilation();
+            CompilationRequestResult r = null;
+            try (DebugContext debug = graalRuntime.openDebugContext(options, task.getCompilationIdentifier(), method, getDebugHandlersFactories());
+                            Activation a = debug.activate()) {
+                r = task.runCompilation(debug);
             }
             assert r != null;
             return r;
         }
     }
 
-    public CompilationResult compile(ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, CompilationIdentifier compilationId, OptionValues options) {
+    public CompilationResult compile(ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, CompilationIdentifier compilationId, OptionValues options, DebugContext debug) {
         HotSpotBackend backend = graalRuntime.getHostBackend();
         HotSpotProviders providers = backend.getProviders();
         final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
-        StructuredGraph graph = method.isNative() || isOSR ? null : getIntrinsicGraph(method, providers, compilationId, options);
+        StructuredGraph graph = method.isNative() || isOSR ? null : getIntrinsicGraph(method, providers, compilationId, options, debug);
 
         if (graph == null) {
             SpeculationLog speculationLog = method.getSpeculationLog();
             if (speculationLog != null) {
                 speculationLog.collectFailedSpeculations();
             }
-            graph = new StructuredGraph.Builder(options, AllowAssumptions.ifTrue(OptAssumptions.getValue(options))).method(method).entryBCI(entryBCI).speculationLog(
+            graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.ifTrue(OptAssumptions.getValue(options))).method(method).entryBCI(entryBCI).speculationLog(
                             speculationLog).useProfilingInfo(useProfilingInfo).compilationId(compilationId).build();
         }
 
@@ -186,17 +194,18 @@
      * @param method
      * @param compilationId
      * @param options
+     * @param debug
      * @return an intrinsic graph that can be compiled and installed for {@code method} or null
      */
     @SuppressWarnings("try")
-    public StructuredGraph getIntrinsicGraph(ResolvedJavaMethod method, HotSpotProviders providers, CompilationIdentifier compilationId, OptionValues options) {
+    public StructuredGraph getIntrinsicGraph(ResolvedJavaMethod method, HotSpotProviders providers, CompilationIdentifier compilationId, OptionValues options, DebugContext debug) {
         Replacements replacements = providers.getReplacements();
         Bytecode subst = replacements.getSubstitutionBytecode(method);
         if (subst != null) {
             ResolvedJavaMethod substMethod = subst.getMethod();
             assert !substMethod.equals(method);
-            StructuredGraph graph = new StructuredGraph.Builder(options, AllowAssumptions.YES).method(substMethod).compilationId(compilationId).build();
-            try (Debug.Scope scope = Debug.scope("GetIntrinsicGraph", graph)) {
+            StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(substMethod).compilationId(compilationId).build();
+            try (DebugContext.Scope scope = debug.scope("GetIntrinsicGraph", graph)) {
                 Plugins plugins = new Plugins(providers.getGraphBuilderPlugins());
                 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
                 IntrinsicContext initialReplacementContext = new IntrinsicContext(method, substMethod, subst.getOrigin(), ROOT_COMPILATION);
@@ -205,7 +214,7 @@
                 assert !graph.isFrozen();
                 return graph;
             } catch (Throwable e) {
-                Debug.handle(e);
+                debug.handle(e);
             }
         }
         return null;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java	Fri Jul 07 09:40:47 2017 -0700
@@ -54,7 +54,8 @@
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.ResolvedJavaType;
 import jdk.vm.ci.runtime.JVMCI;
-import org.graalvm.compiler.debug.GraalDebugConfig;
+
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.options.OptionDescriptor;
 import org.graalvm.compiler.options.OptionDescriptors;
 import org.graalvm.compiler.options.OptionKey;
@@ -165,9 +166,9 @@
             for (Dump request : methodDumps) {
                 final String clazzName = method.getDeclaringClass().getName();
                 if (method.getName().equals(request.method) && clazzName.equals(request.clazz)) {
-                    current = new OptionValues(current, GraalDebugConfig.Options.Dump, request.filter,
-                                    GraalDebugConfig.Options.PrintGraphHost, request.host,
-                                    GraalDebugConfig.Options.PrintBinaryGraphPort, request.port);
+                    current = new OptionValues(current, DebugOptions.Dump, request.filter,
+                                    DebugOptions.PrintGraphHost, request.host,
+                                    DebugOptions.PrintBinaryGraphPort, request.port);
                     break;
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,12 +27,7 @@
 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
 import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining;
-import static org.graalvm.compiler.debug.GraalDebugConfig.areScopedGlobalMetricsEnabled;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueSummary;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Dump;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Log;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.MethodFilter;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.Verify;
+import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -54,14 +49,15 @@
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.api.runtime.GraalRuntime;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.target.Backend;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugEnvironment;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Description;
+import org.graalvm.compiler.debug.GlobalMetrics;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.DebugValuesPrinter;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter;
 import org.graalvm.compiler.hotspot.CompilationStatistics.Options;
 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory.BackendMap;
 import org.graalvm.compiler.hotspot.debug.BenchmarkCounters;
@@ -104,7 +100,7 @@
     }
 
     private final HotSpotBackend hostBackend;
-    private DebugValuesPrinter debugValuesPrinter;
+    private final GlobalMetrics metricValues = new GlobalMetrics();
     private final List<SnippetCounter.Group> snippetCounterGroups;
 
     private final EconomicMap<Class<? extends Architecture>, HotSpotBackend> backends = EconomicMap.create(Equivalence.IDENTITY);
@@ -163,56 +159,6 @@
             }
         }
 
-        if (Log.getValue(options) == null && !areScopedGlobalMetricsEnabled(options) && Dump.getValue(options) == null && Verify.getValue(options) == null) {
-            if (MethodFilter.getValue(options) != null && !Debug.isEnabled()) {
-                TTY.println("WARNING: Ignoring MethodFilter option since Log, Meter, Time, TrackMemUse, Dump and Verify options are all null");
-            }
-        }
-
-        if (Debug.isEnabled()) {
-            DebugEnvironment.ensureInitialized(options, hostBackend.getProviders().getSnippetReflection());
-
-            String summary = DebugValueSummary.getValue(options);
-            if (summary != null) {
-                switch (summary) {
-                    case "Name":
-                    case "Partial":
-                    case "Complete":
-                    case "Thread":
-                        break;
-                    default:
-                        throw new GraalError("Unsupported value for DebugSummaryValue: %s", summary);
-                }
-            }
-        }
-
-        if (Debug.areUnconditionalCountersEnabled() || Debug.areUnconditionalTimersEnabled() || Debug.areUnconditionalMethodMetricsEnabled() ||
-                        (Debug.isEnabled() && areScopedGlobalMetricsEnabled(options)) || (Debug.isEnabled() && Debug.isMethodFilteringEnabled())) {
-            // This must be created here to avoid loading the DebugValuesPrinter class
-            // during shutdown() which in turn can cause a deadlock
-            int mmPrinterType = 0;
-            mmPrinterType |= MethodMetricsPrinter.Options.MethodMeterPrintAscii.getValue(options) ? 1 : 0;
-            mmPrinterType |= MethodMetricsPrinter.Options.MethodMeterFile.getValue(options) != null ? 2 : 0;
-            switch (mmPrinterType) {
-                case 0:
-                    debugValuesPrinter = new DebugValuesPrinter();
-                    break;
-                case 1:
-                    debugValuesPrinter = new DebugValuesPrinter(new MethodMetricsPrinter.MethodMetricsASCIIPrinter(TTY.out));
-                    break;
-                case 2:
-                    debugValuesPrinter = new DebugValuesPrinter(new MethodMetricsPrinter.MethodMetricsCSVFilePrinter());
-                    break;
-                case 3:
-                    debugValuesPrinter = new DebugValuesPrinter(
-                                    new MethodMetricsPrinter.MethodMetricsCompositePrinter(new MethodMetricsPrinter.MethodMetricsCSVFilePrinter(),
-                                                    new MethodMetricsPrinter.MethodMetricsASCIIPrinter(TTY.out)));
-                    break;
-                default:
-                    break;
-            }
-        }
-
         // Complete initialization of backends
         try (InitTimer st = timer(hostBackend.getTarget().arch.getName(), ".completeInitialization")) {
             hostBackend.completeInitialization(jvmciRuntime, options);
@@ -262,6 +208,12 @@
     }
 
     @Override
+    public DebugContext openDebugContext(OptionValues compilationOptions, CompilationIdentifier compilationId, Object compilable, Iterable<DebugHandlersFactory> factories) {
+        Description description = new Description(compilable, compilationId.toString(CompilationIdentifier.Verbosity.ID));
+        return DebugContext.create(compilationOptions, description, metricValues, DEFAULT_LOG_STREAM, factories);
+    }
+
+    @Override
     public OptionValues getOptions() {
         return mBean.optionsFor(options, null);
     }
@@ -330,9 +282,8 @@
 
     void shutdown() {
         shutdown = true;
-        if (debugValuesPrinter != null) {
-            debugValuesPrinter.printDebugValues(options);
-        }
+        metricValues.print(options);
+
         phaseTransition("final");
 
         if (snippetCounterGroups != null) {
@@ -345,10 +296,8 @@
         archiveAndDeleteOutputDirectory();
     }
 
-    void clearMeters() {
-        if (debugValuesPrinter != null) {
-            debugValuesPrinter.clearDebugValues();
-        }
+    void clearMetrics() {
+        metricValues.clear();
     }
 
     private final boolean bootstrapJVMCI;
@@ -449,15 +398,13 @@
                     });
                     TTY.println("Graal diagnostic output saved in %s", zip);
                 } catch (IOException e) {
-                    TTY.printf("IO error archiving %s:%n", dir);
-                    e.printStackTrace(TTY.out);
+                    TTY.printf("IO error archiving %s:%n%s%n", dir, e);
                 }
                 for (Path p : toDelete) {
                     try {
                         Files.delete(p);
                     } catch (IOException e) {
-                        TTY.printf("IO error deleting %s:%n", p);
-                        e.printStackTrace(TTY.out);
+                        TTY.printf("IO error deleting %s:%n%s%n", p, e);
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntimeProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntimeProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,6 +23,9 @@
 package org.graalvm.compiler.hotspot;
 
 import org.graalvm.compiler.api.runtime.GraalRuntime;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.replacements.SnippetCounter.Group;
@@ -55,19 +58,27 @@
     GraalHotSpotVMConfig getVMConfig();
 
     /**
+     * Opens a debug context for compiling {@code compilable}. The {@link DebugContext#close()}
+     * method should be called on the returned object once the compilation is finished.
+     *
+     * @param compilationOptions the options used to configure the compilation debug context
+     * @param compilationId a system wide unique compilation id
+     * @param compilable the input to the compilation
+     */
+    DebugContext openDebugContext(OptionValues compilationOptions, CompilationIdentifier compilationId, Object compilable, Iterable<DebugHandlersFactory> factories);
+
+    /**
      * Gets the option values associated with this runtime.
      */
     OptionValues getOptions();
 
     /**
-     * Gets the option values associated with this runtime that are applicable for given method.
+     * Gets the option values associated with this runtime that are applicable for a given method.
      *
      * @param forMethod the method we are seeking for options for
-     * @return the options - by default same as {@link #getOptions()}
+     * @return the options applicable for compiling {@code method}
      */
-    default OptionValues getOptions(ResolvedJavaMethod forMethod) {
-        return getOptions();
-    }
+    OptionValues getOptions(ResolvedJavaMethod forMethod);
 
     /**
      * Determines if the VM is currently bootstrapping the JVMCI compiler.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalVMEventListener.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalVMEventListener.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,17 +22,18 @@
  */
 package org.graalvm.compiler.hotspot;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.serviceprovider.GraalServices;
+
 import jdk.vm.ci.code.CompiledCode;
 import jdk.vm.ci.code.InstalledCode;
 import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
 import jdk.vm.ci.hotspot.HotSpotVMEventListener;
-import org.graalvm.compiler.code.CompilationResult;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.serviceprovider.GraalServices;
-
-import java.util.ArrayList;
-import java.util.List;
 
 public class HotSpotGraalVMEventListener implements HotSpotVMEventListener {
 
@@ -54,13 +55,14 @@
 
     @Override
     public void notifyInstall(HotSpotCodeCacheProvider codeCache, InstalledCode installedCode, CompiledCode compiledCode) {
-        if (Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
-            CompilationResult compResult = Debug.contextLookup(CompilationResult.class);
+        DebugContext debug = DebugContext.forCurrentThread();
+        if (debug.isDumpEnabled(DebugContext.BASIC_LEVEL)) {
+            CompilationResult compResult = debug.contextLookup(CompilationResult.class);
             assert compResult != null : "can't dump installed code properly without CompilationResult";
-            Debug.dump(Debug.BASIC_LEVEL, installedCode, "After code installation");
+            debug.dump(DebugContext.BASIC_LEVEL, installedCode, "After code installation");
         }
-        if (Debug.isLogEnabled()) {
-            Debug.log("%s", codeCache.disassemble(installedCode));
+        if (debug.isLogEnabled()) {
+            debug.log("%s", codeCache.disassemble(installedCode));
         }
         for (HotSpotCodeCacheListener listener : listeners) {
             listener.notifyInstall(codeCache, installedCode, compiledCode);
@@ -70,8 +72,8 @@
     @Override
     public void notifyBootstrapFinished() {
         runtime.notifyBootstrapFinished();
-        if (GraalDebugConfig.Options.ClearMetricsAfterBootstrap.getValue(runtime.getOptions())) {
-            runtime.clearMeters();
+        if (DebugOptions.ClearMetricsAfterBootstrap.getValue(runtime.getOptions())) {
+            runtime.clearMetrics();
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotHostBackend.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotHostBackend.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,11 @@
 import static jdk.vm.ci.code.CodeUtil.getCallingConvention;
 import static jdk.vm.ci.common.InitTimer.timer;
 
+import java.util.Collections;
+
 import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
 import org.graalvm.compiler.hotspot.meta.HotSpotLoweringProvider;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -36,6 +39,7 @@
 import org.graalvm.compiler.lir.framemap.ReferenceMapBuilder;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 
 import jdk.vm.ci.code.CallingConvention;
 import jdk.vm.ci.common.InitTimer;
@@ -77,7 +81,8 @@
             foreignCalls.initialize(providers, options);
         }
         try (InitTimer st = timer("lowerer.initialize")) {
-            lowerer.initialize(options, providers, config);
+            Iterable<DebugHandlersFactory> factories = Collections.singletonList(new GraalDebugHandlersFactory(providers.getSnippetReflection()));
+            lowerer.initialize(options, factories, providers, config);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotReplacementsImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotReplacementsImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import org.graalvm.compiler.hotspot.word.HotSpotOperation;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.util.Providers;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 import org.graalvm.compiler.replacements.ReplacementsImpl;
 
 import jdk.vm.ci.code.TargetDescription;
@@ -39,7 +40,7 @@
 public class HotSpotReplacementsImpl extends ReplacementsImpl {
 
     public HotSpotReplacementsImpl(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target) {
-        super(options, providers, snippetReflection, bytecodeProvider, target);
+        super(options, new GraalDebugHandlersFactory(snippetReflection), providers, snippetReflection, bytecodeProvider, target);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotRetryableCompilation.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotRetryableCompilation.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,31 +22,23 @@
  */
 package org.graalvm.compiler.hotspot;
 
-import static org.graalvm.compiler.debug.Debug.VERBOSE_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.PrintGraphFileName;
+import static org.graalvm.compiler.debug.DebugContext.VERBOSE_LEVEL;
+import static org.graalvm.compiler.debug.DebugOptions.Dump;
+import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
+import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collection;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugDumpHandler;
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugRetryableTask;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.printer.GraalDebugConfigCustomizer;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 
 import jdk.vm.ci.code.BailoutException;
 
@@ -56,12 +48,10 @@
  */
 public abstract class HotSpotRetryableCompilation<T> extends DebugRetryableTask<T> {
 
-    protected final OptionValues originalOptions;
     protected final HotSpotGraalRuntimeProvider runtime;
 
-    public HotSpotRetryableCompilation(HotSpotGraalRuntimeProvider runtime, OptionValues options) {
+    public HotSpotRetryableCompilation(HotSpotGraalRuntimeProvider runtime) {
         this.runtime = runtime;
-        this.originalOptions = options;
     }
 
     /**
@@ -70,65 +60,42 @@
     @Override
     public abstract String toString();
 
-    private static String sanitizedFileName(String name) {
-        StringBuilder buf = new StringBuilder(name.length());
-        for (int i = 0; i < name.length(); i++) {
-            char c = name.charAt(i);
-            try {
-                Paths.get(String.valueOf(c));
-            } catch (InvalidPathException e) {
-                buf.append('_');
-            }
-            buf.append(c);
-        }
-        return buf.toString();
-    }
-
     @Override
-    protected boolean onRetry(Throwable t) {
+    protected DebugContext getRetryContext(DebugContext initialDebug, Throwable t) {
         if (t instanceof BailoutException) {
-            return false;
+            return null;
         }
 
-        if (!Debug.isEnabled()) {
-            TTY.printf("Error while compiling %s due to %s.%nRe-run with -D%s%s=true to capture graph dumps upon a compilation failure.%n", this,
-                            t, HotSpotGraalOptionValues.GRAAL_OPTION_PROPERTY_PREFIX, ForceDebugEnable.getName());
-            return false;
-        }
-
-        if (Dump.hasBeenSet(originalOptions)) {
+        OptionValues initialOptions = initialDebug.getOptions();
+        if (Dump.hasBeenSet(initialOptions)) {
             // If dumping is explicitly enabled, Graal is being debugged
             // so don't interfere with what the user is expecting to see.
-            return false;
+            return null;
         }
 
         String outputDirectory = runtime.getOutputDirectory();
         if (outputDirectory == null) {
-            return false;
+            return null;
         }
-        String dumpName = sanitizedFileName(toString());
+        String dumpName = GraalDebugHandlersFactory.sanitizedFileName(toString());
         File dumpPath = new File(outputDirectory, dumpName);
         dumpPath.mkdirs();
         if (!dumpPath.exists()) {
             TTY.println("Warning: could not create dump directory " + dumpPath);
-            return false;
+            return null;
         }
 
         TTY.println("Retrying compilation of " + this + " due to " + t);
         retryLogPath = new File(dumpPath, "retry.log").getPath();
         log("Exception causing retry", t);
-        retryDumpHandlers = new ArrayList<>();
-        retryOptions = new OptionValues(originalOptions,
-                        PrintCFGFileName, dumpName,
-                        PrintGraphFileName, dumpName,
+        OptionValues retryOptions = new OptionValues(initialOptions,
+                        Dump, ":" + VERBOSE_LEVEL,
+                        MethodFilter, null,
                         DumpPath, dumpPath.getPath());
-        override(DUMP, VERBOSE_LEVEL).enable(DUMP_METHOD);
-        new GraalDebugConfigCustomizer().customize(this);
-        return true;
+        SnippetReflectionProvider snippetReflection = runtime.getHostProviders().getSnippetReflection();
+        return DebugContext.create(retryOptions, new GraalDebugHandlersFactory(snippetReflection));
     }
 
-    private Collection<DebugDumpHandler> retryDumpHandlers;
-    private OptionValues retryOptions;
     private String retryLogPath;
 
     /**
@@ -153,14 +120,4 @@
             }
         }
     }
-
-    @Override
-    public Collection<DebugDumpHandler> dumpHandlers() {
-        return retryDumpHandlers;
-    }
-
-    @Override
-    public OptionValues getOptions() {
-        return retryOptions;
-    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java	Fri Jul 07 09:40:47 2017 -0700
@@ -38,11 +38,11 @@
 class JVMCIVersionCheck {
 
     private static final int JVMCI8_MIN_MAJOR_VERSION = 0;
-    private static final int JVMCI8_MIN_MINOR_VERSION = 26;
+    private static final int JVMCI8_MIN_MINOR_VERSION = 29;
 
     // MAX_VALUE indicates that no current EA version is compatible with Graal.
     // Note: Keep README.md in sync with the EA version support checked here.
-    private static final int JVMCI9_MIN_EA_BUILD = 174;
+    private static final int JVMCI9_MIN_EA_BUILD = 176;
 
     private static void failVersionCheck(boolean exit, String reason, Object... args) {
         Formatter errorMessage = new Formatter().format(reason, args);
@@ -112,7 +112,7 @@
                             "Cannot read JVMCI version from java.vm.version property: %s.%n", vmVersion);
         } else {
             if (vmVersion.contains("SNAPSHOT")) {
-                // The snapshot of http://hg.openjdk.java.net/jdk9/hs tip is expected to work
+                // The snapshot of http://hg.openjdk.java.net/jdk9/dev tip is expected to work
                 return;
             }
             if (vmVersion.contains("internal")) {
@@ -120,9 +120,8 @@
                 return;
             }
             // http://openjdk.java.net/jeps/223
-            // Only support EA builds until GA is available
-            if (vmVersion.startsWith("9-ea+")) {
-                int start = "9-ea+".length();
+            if (vmVersion.startsWith("9+")) {
+                int start = "9+".length();
                 int end = start;
                 end = start;
                 while (end < vmVersion.length() && Character.isDigit(vmVersion.charAt(end))) {
@@ -145,9 +144,10 @@
                     failVersionCheck(exitOnFailure, "The VM is an insufficiently recent EA JDK9 build for Graal: %d < %d.%n", build, JVMCI9_MIN_EA_BUILD);
                 }
                 return;
+            } else {
+                // Graal will be compatible with all JDK versions as of 9 GA
+                // until a JVMCI API change is made in a 9u or later release.
             }
-            failVersionCheck(exitOnFailure, "The VM does not support the minimum JVMCI API version required by Graal.%n" +
-                            "Cannot read JDK9 EA build number from java.vm.version property: %s.%n", vmVersion);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/PrintStreamOptionKey.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/PrintStreamOptionKey.java	Fri Jul 07 09:40:47 2017 -0700
@@ -65,6 +65,9 @@
 
             }
         }
+        if (name.contains("%t")) {
+            name = name.replaceAll("%t", String.valueOf(System.currentTimeMillis()));
+        }
         return name;
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,10 @@
  */
 package org.graalvm.compiler.hotspot.debug;
 
-import java.io.FileNotFoundException;
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintStream;
-import java.nio.file.Path;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -39,7 +38,6 @@
 
 import org.graalvm.compiler.core.common.SuppressFBWarnings;
 import org.graalvm.compiler.debug.CSVUtil;
-import org.graalvm.compiler.debug.GraalDebugConfig;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
@@ -49,7 +47,6 @@
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.options.UniquePathUtilities;
 
 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
 
@@ -105,9 +102,9 @@
         public static final OptionKey<String> BenchmarkDynamicCounters = new OptionKey<>(null);
         @Option(help = "Use grouping separators for number printing", type = OptionType.Debug)
         public static final OptionKey<Boolean> DynamicCountersPrintGroupSeparator = new OptionKey<>(true);
-        @Option(help = "Print in human readable format", type = OptionType.Debug)
-        public static final OptionKey<Boolean> DynamicCountersHumanReadable = new OptionKey<>(true);
-        @Option(help = "Benchmark counters log file (default is stdout)", type = OptionType.Debug)
+        @Option(help = "File to which benchmark counters are dumped. A CSV format is used if the file ends with .csv " +
+                       "otherwise a more human readable format is used. The fields in the CSV format are: " +
+                       "category, group, name, value", type = OptionType.Debug)
         public static final OptionKey<String> BenchmarkCountersFile = new OptionKey<>(null);
         @Option(help = "Dump dynamic counters", type = OptionType.Debug)
         public static final OptionKey<Boolean> BenchmarkCountersDumpDynamic = new OptionKey<>(true);
@@ -170,112 +167,153 @@
 
     private static synchronized void dump(OptionValues options, PrintStream out, double seconds, long[] counters, int maxRows) {
         if (!counterMap.isEmpty()) {
-            if (Options.DynamicCountersHumanReadable.getValue(options)) {
-                out.println("====== dynamic counters (" + counterMap.size() + " in total) ======");
-            }
-            TreeSet<String> set = new TreeSet<>();
-            counterMap.forEach((nameGroup, counter) -> set.add(counter.group));
-            for (String group : set) {
-                if (group != null) {
-                    if (Options.BenchmarkCountersDumpStatic.getValue(options)) {
-                        dumpCounters(options, out, seconds, counters, true, group, maxRows);
-                    }
-                    if (Options.BenchmarkCountersDumpDynamic.getValue(options)) {
-                        dumpCounters(options, out, seconds, counters, false, group, maxRows);
+            try (Dumper dumper = Dumper.getDumper(options, out, counterMap.size(), seconds, maxRows)) {
+                TreeSet<String> set = new TreeSet<>();
+                counterMap.forEach((nameGroup, counter) -> set.add(counter.group));
+                for (String group : set) {
+                    if (group != null) {
+                        if (Options.BenchmarkCountersDumpStatic.getValue(options)) {
+                            dumper.dumpCounters(true, group, collectStaticCounters(), counterMap.entrySet(), options);
+                        }
+                        if (Options.BenchmarkCountersDumpDynamic.getValue(options)) {
+                            dumper.dumpCounters(false, group, collectDynamicCounters(counters), counterMap.entrySet(), options);
+                        }
                     }
                 }
             }
-            if (Options.DynamicCountersHumanReadable.getValue(options)) {
-                out.println("============================");
-            }
 
             clear(counters);
         }
     }
 
+    private static synchronized long[] collectDynamicCounters(long[] counters) {
+        long[] array = counters.clone();
+        for (int i = 0; i < array.length; i++) {
+            array[i] -= delta[i];
+        }
+        return array;
+    }
+
+    private static synchronized long[] collectStaticCounters() {
+        long[] array = new long[counterMap.size()];
+        for (Counter counter : counterMap.values()) {
+            array[counter.index] = counter.staticCounters.get();
+        }
+        return array;
+    }
+
     private static synchronized void clear(long[] counters) {
         delta = counters;
     }
 
-    private static synchronized void dumpCounters(OptionValues options, PrintStream out, double seconds, long[] counters, boolean staticCounter, String group, int maxRows) {
+    private static boolean shouldDumpComputerReadable(OptionValues options) {
+        String dumpFile = Options.BenchmarkCountersFile.getValue(options);
+        return dumpFile != null && (dumpFile.endsWith(".csv") || dumpFile.endsWith(".CSV"));
+    }
+
+    private abstract static class Dumper implements AutoCloseable {
+        public static Dumper getDumper(OptionValues options, PrintStream out, int counterSize, double second, int maxRows) {
+            Dumper dumper = shouldDumpComputerReadable(options) ? new ComputerReadableDumper(out) : new HumanReadableDumper(out, second, maxRows);
+            dumper.start(counterSize);
+            return dumper;
+        }
 
-        // collect the numbers
-        long[] array;
-        if (staticCounter) {
-            array = new long[counterMap.size()];
-            for (Counter counter : counterMap.values()) {
-                array[counter.index] = counter.staticCounters.get();
-            }
-        } else {
-            array = counters.clone();
-            for (int i = 0; i < array.length; i++) {
-                array[i] -= delta[i];
-            }
+        protected final PrintStream out;
+
+        private Dumper(PrintStream out) {
+            this.out = out;
         }
-        Set<Entry<String, Counter>> counterEntrySet = counterMap.entrySet();
-        if (Options.DynamicCountersHumanReadable.getValue(options)) {
-            dumpHumanReadable(options, out, seconds, staticCounter, group, maxRows, array, counterEntrySet);
-        } else {
-            dumpComputerReadable(out, staticCounter, group, array, counterEntrySet);
-        }
+
+        protected abstract void start(int size);
+
+        public abstract void dumpCounters(boolean staticCounter, String group, long[] array, Set<Entry<String, Counter>> counterEntrySet, OptionValues options);
+
+        @Override
+        public abstract void close();
+
     }
 
     private static String getName(String nameGroup, String group) {
         return nameGroup.substring(0, nameGroup.length() - group.length() - 1);
     }
 
-    private static void dumpHumanReadable(OptionValues options, PrintStream out, double seconds, boolean staticCounter, String group, int maxRows, long[] array,
-                    Set<Entry<String, Counter>> counterEntrySet) {
-        // sort the counters by putting them into a sorted map
-        TreeMap<Long, String> sorted = new TreeMap<>();
-        long sum = 0;
-        for (Map.Entry<String, Counter> entry : counterEntrySet) {
-            Counter counter = entry.getValue();
-            int index = counter.index;
-            if (counter.group.equals(group)) {
-                sum += array[index];
-                sorted.put(array[index] * array.length + index, getName(entry.getKey(), group));
-            }
+    private static long percentage(long counter, long sum) {
+        return (counter * 200 + 1) / sum / 2;
+    }
+
+    private static class HumanReadableDumper extends Dumper {
+        private final double seconds;
+        private final int maxRows;
+
+        HumanReadableDumper(PrintStream out, double seconds, int maxRows) {
+            super(out);
+            this.seconds = seconds;
+            this.maxRows = maxRows;
+        }
+
+        @Override
+        public void start(int size) {
+            out.println("====== dynamic counters (" + size + " in total) ======");
+        }
+
+        @Override
+        public void close() {
+            out.println("============================");
         }
 
-        if (sum > 0) {
-            long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100);
-            int cnt = sorted.size();
-
-            // remove everything below cutoff and keep at most maxRows
-            Iterator<Map.Entry<Long, String>> iter = sorted.entrySet().iterator();
-            while (iter.hasNext()) {
-                Map.Entry<Long, String> entry = iter.next();
-                long counter = entry.getKey() / array.length;
-                if (counter < cutoff || cnt > maxRows) {
-                    iter.remove();
+        @Override
+        public void dumpCounters(boolean staticCounter, String group, long[] array, Set<Entry<String, Counter>> counterEntrySet, OptionValues options) {
+            // sort the counters by putting them into a sorted map
+            TreeMap<Long, String> sorted = new TreeMap<>();
+            long sum = 0;
+            for (Map.Entry<String, Counter> entry : counterEntrySet) {
+                Counter counter = entry.getValue();
+                int index = counter.index;
+                if (counter.group.equals(group)) {
+                    sum += array[index];
+                    sorted.put(array[index] * array.length + index, getName(entry.getKey(), group));
                 }
-                cnt--;
             }
 
-            String numFmt = Options.DynamicCountersPrintGroupSeparator.getValue(options) ? "%,19d" : "%19d";
-            if (staticCounter) {
-                out.println("=========== " + group + " (static counters):");
-                for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+            if (sum > 0) {
+                long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100);
+                int cnt = sorted.size();
+
+                // remove everything below cutoff and keep at most maxRows
+                Iterator<Map.Entry<Long, String>> iter = sorted.entrySet().iterator();
+                while (iter.hasNext()) {
+                    Map.Entry<Long, String> entry = iter.next();
                     long counter = entry.getKey() / array.length;
-                    out.format(Locale.US, numFmt + " %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
+                    if (counter < cutoff || cnt > maxRows) {
+                        iter.remove();
+                    }
+                    cnt--;
                 }
-                out.format(Locale.US, numFmt + " total\n", sum);
-            } else {
-                if (group.startsWith("~")) {
-                    out.println("=========== " + group + " (dynamic counters), time = " + seconds + " s:");
-                    for (Map.Entry<Long, String> entry : sorted.entrySet()) {
-                        long counter = entry.getKey() / array.length;
-                        out.format(Locale.US, numFmt + "/s %3d%%  %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue());
-                    }
-                    out.format(Locale.US, numFmt + "/s total\n", (long) (sum / seconds));
-                } else {
-                    out.println("=========== " + group + " (dynamic counters):");
+
+                String numFmt = Options.DynamicCountersPrintGroupSeparator.getValue(options) ? "%,19d" : "%19d";
+                if (staticCounter) {
+                    out.println("=========== " + group + " (static counters):");
                     for (Map.Entry<Long, String> entry : sorted.entrySet()) {
                         long counter = entry.getKey() / array.length;
                         out.format(Locale.US, numFmt + " %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
                     }
                     out.format(Locale.US, numFmt + " total\n", sum);
+                } else {
+                    if (group.startsWith("~")) {
+                        out.println("=========== " + group + " (dynamic counters), time = " + seconds + " s:");
+                        for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+                            long counter = entry.getKey() / array.length;
+                            out.format(Locale.US, numFmt + "/s %3d%%  %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue());
+                        }
+                        out.format(Locale.US, numFmt + "/s total\n", (long) (sum / seconds));
+                    } else {
+                        out.println("=========== " + group + " (dynamic counters):");
+                        for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+                            long counter = entry.getKey() / array.length;
+                            out.format(Locale.US, numFmt + " %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
+                        }
+                        out.format(Locale.US, numFmt + " total\n", sum);
+                    }
                 }
             }
         }
@@ -283,23 +321,37 @@
 
     private static final String CSV_FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%d");
 
-    private static void dumpComputerReadable(PrintStream out, boolean staticCounter, String group, long[] array, Set<Entry<String, Counter>> counterEntrySet) {
-        String category = staticCounter ? "static counters" : "dynamic counters";
-        for (Map.Entry<String, Counter> entry : counterEntrySet) {
-            Counter counter = entry.getValue();
-            if (counter.group.equals(group)) {
-                String name = getName(entry.getKey(), group);
-                int index = counter.index;
-                long value = array[index];
-                CSVUtil.Escape.println(out, CSV_FMT, category, group, name, value);
+    private static class ComputerReadableDumper extends Dumper {
+
+        ComputerReadableDumper(PrintStream out) {
+            super(out);
+        }
+
+        @Override
+        public void start(int size) {
+            // do nothing
+        }
+
+        @Override
+        public void close() {
+            // do nothing
+        }
+
+        @Override
+        public void dumpCounters(boolean staticCounter, String group, long[] array, Set<Entry<String, Counter>> counterEntrySet, OptionValues options) {
+            String category = staticCounter ? "static counters" : "dynamic counters";
+            for (Map.Entry<String, Counter> entry : counterEntrySet) {
+                Counter counter = entry.getValue();
+                if (counter.group.equals(group)) {
+                    String name = getName(entry.getKey(), group);
+                    int index = counter.index;
+                    long value = array[index];
+                    CSVUtil.Escape.println(out, CSV_FMT, category, group, name, value);
+                }
             }
         }
     }
 
-    private static long percentage(long counter, long sum) {
-        return (counter * 200 + 1) / sum / 2;
-    }
-
     private abstract static class CallbackOutputStream extends OutputStream {
 
         protected final PrintStream delegate;
@@ -440,10 +492,11 @@
     private static PrintStream getPrintStream(OptionValues options) {
         if (Options.BenchmarkCountersFile.getValue(options) != null) {
             try {
-                Path path = UniquePathUtilities.getPathGlobal(options, Options.BenchmarkCountersFile, GraalDebugConfig.Options.DumpPath, "csv");
-                TTY.println("Writing benchmark counters to '%s'", path);
-                return new PrintStream(path.toFile());
-            } catch (FileNotFoundException e) {
+
+                File file = new File(Options.BenchmarkCountersFile.getValue(options));
+                TTY.println("Writing benchmark counters to '%s'", file.getAbsolutePath());
+                return new PrintStream(file);
+            } catch (IOException e) {
                 TTY.out().println(e.getMessage());
                 TTY.out().println("Fallback to default");
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/lir/HotSpotZapRegistersPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/lir/HotSpotZapRegistersPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,7 +27,7 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.hotspot.HotSpotLIRGenerationResult;
 import org.graalvm.compiler.hotspot.stubs.Stub;
@@ -82,7 +82,8 @@
     @SuppressWarnings("try")
     private static void processBlock(DiagnosticLIRGeneratorTool diagnosticLirGenTool, HotSpotLIRGenerationResult res, LIR lir, LIRInsertionBuffer buffer, AbstractBlockBase<?> block,
                     boolean zapRegisters, boolean zapStack) {
-        try (Indent indent = Debug.logAndIndent("Process block %s", block)) {
+        DebugContext debug = lir.getDebug();
+        try (Indent indent = debug.logAndIndent("Process block %s", block)) {
             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
             buffer.init(instructions);
             for (int index = 0; index < instructions.size(); index++) {
@@ -100,7 +101,7 @@
                         SaveRegistersOp old = res.getCalleeSaveInfo().put(state, zap);
                         assert old == null : "Already another SaveRegisterOp registered! " + old;
                         buffer.append(index + 1, (LIRInstruction) zap);
-                        Debug.log("Insert ZapRegister after %s", inst);
+                        debug.log("Insert ZapRegister after %s", inst);
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -49,6 +49,7 @@
 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.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
@@ -199,25 +200,25 @@
     }
 
     @Override
-    public void initialize(OptionValues options, HotSpotProviders providers, GraalHotSpotVMConfig config) {
-        super.initialize(options, runtime, providers, providers.getSnippetReflection());
+    public void initialize(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, GraalHotSpotVMConfig config) {
+        super.initialize(options, factories, runtime, providers, providers.getSnippetReflection());
 
         assert target == providers.getCodeCache().getTarget();
-        instanceofSnippets = new InstanceOfSnippets.Templates(options, runtime, providers, target);
-        newObjectSnippets = new NewObjectSnippets.Templates(options, runtime, providers, target, config);
-        monitorSnippets = new MonitorSnippets.Templates(options, runtime, providers, target, config.useFastLocking);
-        writeBarrierSnippets = new WriteBarrierSnippets.Templates(options, runtime, providers, target, config.useCompressedOops ? config.getOopEncoding() : null);
-        exceptionObjectSnippets = new LoadExceptionObjectSnippets.Templates(options, providers, target);
-        unsafeLoadSnippets = new UnsafeLoadSnippets.Templates(options, providers, target);
-        assertionSnippets = new AssertionSnippets.Templates(options, providers, target);
-        arraycopySnippets = new ArrayCopySnippets.Templates(options, runtime, providers, target);
-        stringToBytesSnippets = new StringToBytesSnippets.Templates(options, providers, target);
-        hashCodeSnippets = new HashCodeSnippets.Templates(options, providers, target);
+        instanceofSnippets = new InstanceOfSnippets.Templates(options, factories, runtime, providers, target);
+        newObjectSnippets = new NewObjectSnippets.Templates(options, factories, runtime, providers, target, config);
+        monitorSnippets = new MonitorSnippets.Templates(options, factories, runtime, providers, target, config.useFastLocking);
+        writeBarrierSnippets = new WriteBarrierSnippets.Templates(options, factories, runtime, providers, target, config.useCompressedOops ? config.getOopEncoding() : null);
+        exceptionObjectSnippets = new LoadExceptionObjectSnippets.Templates(options, factories, providers, target);
+        unsafeLoadSnippets = new UnsafeLoadSnippets.Templates(options, factories, providers, target);
+        assertionSnippets = new AssertionSnippets.Templates(options, factories, providers, target);
+        arraycopySnippets = new ArrayCopySnippets.Templates(options, factories, runtime, providers, target);
+        stringToBytesSnippets = new StringToBytesSnippets.Templates(options, factories, providers, target);
+        hashCodeSnippets = new HashCodeSnippets.Templates(options, factories, providers, target);
         if (GeneratePIC.getValue(options)) {
-            resolveConstantSnippets = new ResolveConstantSnippets.Templates(options, providers, target);
-            profileSnippets = new ProfileSnippets.Templates(options, providers, target);
+            resolveConstantSnippets = new ResolveConstantSnippets.Templates(options, factories, providers, target);
+            profileSnippets = new ProfileSnippets.Templates(options, factories, providers, target);
         }
-        providers.getReplacements().registerSnippetTemplateCache(new UnsafeArrayCopySnippets.Templates(options, providers, target));
+        providers.getReplacements().registerSnippetTemplateCache(new UnsafeArrayCopySnippets.Templates(options, factories, providers, target));
     }
 
     public MonitorSnippets.Templates getMonitorSnippets() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotAOTProfilingPlugin.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotAOTProfilingPlugin.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,6 @@
  */
 package org.graalvm.compiler.hotspot.meta;
 
-import org.graalvm.compiler.hotspot.FingerprintUtil;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.options.Option;
 import org.graalvm.compiler.options.OptionKey;
@@ -50,7 +49,7 @@
 
     @Override
     public boolean shouldProfile(GraphBuilderContext builder, ResolvedJavaMethod method) {
-        return super.shouldProfile(builder, method) && FingerprintUtil.getFingerprint(((HotSpotResolvedObjectType) method.getDeclaringClass())) != 0;
+        return super.shouldProfile(builder, method) && ((HotSpotResolvedObjectType) method.getDeclaringClass()).getFingerprint() != 0;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotForeignCallsProviderImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotForeignCallsProviderImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -146,8 +146,8 @@
      *            cannot be re-executed.
      * @param killedLocations the memory locations killed by the foreign call
      */
-    public void linkForeignCall(OptionValues options, HotSpotProviders providers, ForeignCallDescriptor descriptor, long address, boolean prependThread, Transition transition,
-                    boolean reexecutable, LocationIdentity... killedLocations) {
+    public void linkForeignCall(OptionValues options, HotSpotProviders providers, ForeignCallDescriptor descriptor, long address, boolean prependThread, Transition transition, boolean reexecutable,
+                    LocationIdentity... killedLocations) {
         ForeignCallStub stub = new ForeignCallStub(options, jvmciRuntime, providers, address, descriptor, prependThread, transition, reexecutable, killedLocations);
         HotSpotForeignCallLinkage linkage = stub.getLinkage();
         HotSpotForeignCallLinkage targetLinkage = stub.getTargetLinkage();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri Jul 07 09:40:47 2017 -0700
@@ -42,7 +42,6 @@
 import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
 import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.hotspot.FingerprintUtil;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.nodes.CurrentJavaThreadNode;
 import org.graalvm.compiler.hotspot.replacements.AESCryptSubstitutions;
@@ -148,7 +147,7 @@
                         return false;
                     }
                     // check if the holder has a valid fingerprint
-                    if (FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) method.getDeclaringClass()) == 0) {
+                    if (((HotSpotResolvedObjectType) method.getDeclaringClass()).getFingerprint() == 0) {
                         // Deopt otherwise
                         b.append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
                         return true;
@@ -163,7 +162,7 @@
                             if (clazz.equals(String.class)) {
                                 return false;
                             }
-                            if (Class.class.isAssignableFrom(clazz) && FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) type) != 0) {
+                            if (Class.class.isAssignableFrom(clazz) && ((HotSpotResolvedObjectType) type).getFingerprint() != 0) {
                                 return false;
                             }
                             b.append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotLoweringProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotLoweringProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.hotspot.meta;
 
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.nodes.spi.LoweringProvider;
 import org.graalvm.compiler.options.OptionValues;
@@ -31,5 +32,5 @@
  */
 public interface HotSpotLoweringProvider extends LoweringProvider {
 
-    void initialize(OptionValues options, HotSpotProviders providers, GraalHotSpotVMConfig config);
+    void initialize(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, GraalHotSpotVMConfig config);
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -133,7 +133,7 @@
             protected void run(StructuredGraph graph, HighTierContext context) {
                 EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, runtime.getTarget().arch);
 
-                StructuredGraph targetGraph = new StructuredGraph.Builder(graph.getOptions(), AllowAssumptions.YES).method(graph.method()).build();
+                StructuredGraph targetGraph = new StructuredGraph.Builder(graph.getOptions(), graph.getDebug(), AllowAssumptions.YES).method(graph.method()).build();
                 SimplifyingGraphDecoder graphDecoder = new SimplifyingGraphDecoder(runtime.getTarget().arch, targetGraph, context.getMetaAccess(), context.getConstantReflection(),
                                 context.getConstantFieldProvider(), context.getStampProvider(), !ImmutableCode.getValue(graph.getOptions()));
                 graphDecoder.decode(encodedGraph);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,8 @@
 import org.graalvm.compiler.core.common.PermanentBailoutException;
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
@@ -80,7 +80,7 @@
         // @formatter:on
     }
 
-    private static final DebugCounter OsrWithLocksCount = Debug.counter("OSRWithLocks");
+    private static final CounterKey OsrWithLocksCount = DebugContext.counter("OSRWithLocks");
 
     private static boolean supportOSRWithLocks(OptionValues options) {
         return Options.SupportOSRWithLocks.getValue(options);
@@ -88,13 +88,14 @@
 
     @Override
     protected void run(StructuredGraph graph) {
+        DebugContext debug = graph.getDebug();
         if (graph.getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI) {
             // This happens during inlining in a OSR method, because the same phase plan will be
             // used.
             assert graph.getNodes(EntryMarkerNode.TYPE).isEmpty();
             return;
         }
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement initial at bci %d", graph.getEntryBCI());
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement initial at bci %d", graph.getEntryBCI());
 
         EntryMarkerNode osr;
         int maxIterations = -1;
@@ -144,7 +145,7 @@
                 proxy.replaceAndDelete(proxy.value());
             }
             GraphUtil.removeFixedWithUnusedInputs(osr);
-            Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement loop peeling result");
+            debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement loop peeling result");
         } while (true);
 
         FrameState osrState = osr.stateAfter();
@@ -157,7 +158,7 @@
         graph.setStart(osrStart);
         osrStart.setStateAfter(osrState);
 
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement after setting OSR start");
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement after setting OSR start");
         final int localsSize = osrState.localsSize();
         final int locksSize = osrState.locksSize();
 
@@ -188,13 +189,13 @@
         }
 
         osr.replaceAtUsages(InputType.Guard, osrStart);
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement after replacing entry proxies");
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement after replacing entry proxies");
         GraphUtil.killCFG(start);
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement result");
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement result");
         new DeadCodeEliminationPhase(Required).apply(graph);
 
         if (currentOSRWithLocks) {
-            OsrWithLocksCount.increment();
+            OsrWithLocksCount.increment(debug);
             for (int i = osrState.monitorIdCount() - 1; i >= 0; --i) {
                 MonitorIdNode id = osrState.monitorIdAt(i);
                 ValueNode lockedObject = osrState.lockAt(i);
@@ -210,7 +211,7 @@
                 osrMonitorEnter.setNext(oldNext);
                 osrStart.setNext(osrMonitorEnter);
             }
-            Debug.dump(Debug.DETAILED_LEVEL, graph, "After inserting OSR monitor enters");
+            debug.dump(DebugContext.DETAILED_LEVEL, graph, "After inserting OSR monitor enters");
             /*
              * Ensure balanced monitorenter - monitorexit
              *
@@ -226,7 +227,7 @@
                 }
             }
         }
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "OnStackReplacement result");
+        debug.dump(DebugContext.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/AOTInliningPolicy.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/AOTInliningPolicy.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,7 +27,6 @@
 
 import java.util.Map;
 
-import org.graalvm.compiler.hotspot.FingerprintUtil;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.options.Option;
@@ -67,7 +66,7 @@
 
         for (int i = 0; i < info.numberOfMethods(); ++i) {
             HotSpotResolvedObjectType t = (HotSpotResolvedObjectType) info.methodAt(i).getDeclaringClass();
-            if (FingerprintUtil.getFingerprint(t) == 0) {
+            if (t.getFingerprint() == 0) {
                 return false;
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -29,14 +29,6 @@
 import java.util.HashSet;
 import java.util.List;
 
-import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
-import jdk.vm.ci.hotspot.HotSpotObjectConstant;
-import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
-import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
-import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
-import jdk.vm.ci.meta.ResolvedJavaType;
-
 import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
@@ -44,7 +36,6 @@
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
-import org.graalvm.compiler.hotspot.FingerprintUtil;
 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction;
 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode;
 import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyFixedNode;
@@ -52,18 +43,37 @@
 import org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode;
 import org.graalvm.compiler.hotspot.nodes.aot.ResolveConstantNode;
 import org.graalvm.compiler.hotspot.nodes.aot.ResolveMethodAndLoadCountersNode;
+import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.phases.BasePhase;
+import org.graalvm.compiler.phases.graph.ReentrantNodeIterator;
+import org.graalvm.compiler.phases.graph.ReentrantNodeIterator.NodeIteratorClosure;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 import org.graalvm.util.EconomicMap;
 
+import jdk.vm.ci.code.BytecodeFrame;
+import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
+import jdk.vm.ci.hotspot.HotSpotObjectConstant;
+import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
+import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
+import jdk.vm.ci.meta.Constant;
+import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
 public class ReplaceConstantNodesPhase extends BasePhase<PhaseContext> {
 
     private static final HashSet<Class<?>> builtIns = new HashSet<>();
@@ -115,63 +125,175 @@
             if (type.getElementalType().isPrimitive()) {
                 return false;
             }
-            return FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) (type.getElementalType())) == 0;
+            return ((HotSpotResolvedObjectType) (type.getElementalType())).getFingerprint() == 0;
         }
-        return FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) type) == 0;
+        return ((HotSpotResolvedObjectType) type).getFingerprint() == 0;
+    }
+
+    /**
+     * Insert the replacement node into the graph. We may need to insert it into a place different
+     * than the original {@link FloatingNode} since we need to make sure that replacement will have
+     * a valid state assigned.
+     *
+     * @param graph
+     * @param stateMapper
+     * @param node
+     * @param replacement
+     */
+    private static void insertReplacement(StructuredGraph graph, FrameStateMapperClosure stateMapper, FloatingNode node, FixedWithNextNode replacement) {
+        FixedWithNextNode insertionPoint = findInsertionPoint(graph, stateMapper, node);
+        graph.addAfterFixed(insertionPoint, replacement);
+        stateMapper.addState(replacement, stateMapper.getState(insertionPoint));
     }
 
     /**
-     * Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with indirection.
+     * Find a good place to insert a stateful fixed node that is above the given node. A good
+     * insertion point should have a valid FrameState reaching it.
      *
      * @param graph
-     * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
-     *            resolution.
+     * @param stateMapper
+     * @param node start search from this node up
+     * @return an insertion point
      */
-    private void handleHotSpotMetaspaceConstant(StructuredGraph graph, ConstantNode node) {
-        HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
-        HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
-
-        if (type != null) {
-            if (verifyFingerprints && checkForBadFingerprint(type)) {
-                throw new GraalError("Type with bad fingerprint: " + type);
-            }
-            assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants";
-            tryToReplaceWithExisting(graph, node);
-            if (anyUsagesNeedReplacement(node)) {
-                replaceWithResolution(graph, node);
-            }
-        } else {
-            throw new GraalError("Unsupported metaspace constant type: " + type);
-        }
+    private static FixedWithNextNode findInsertionPoint(StructuredGraph graph, FrameStateMapperClosure stateMapper, FloatingNode node) {
+        FixedWithNextNode fixed = findFixedBeforeFloating(graph, node);
+        FixedWithNextNode result = findFixedWithValidState(graph, stateMapper, fixed);
+        return result;
     }
 
     /**
-     * Find the lowest dominating {@link FixedWithNextNode} before given node.
+     * Find the first {@link FixedWithNextNode} that is currently scheduled before the given
+     * floating node.
      *
      * @param graph
-     * @param node
-     * @return the last {@link FixedWithNextNode} that is scheduled before node.
+     * @param node start search from this node up
+     * @return the first {@link FixedWithNextNode}
      */
-    private static FixedWithNextNode findFixedWithNextBefore(StructuredGraph graph, Node node) {
+    private static FixedWithNextNode findFixedBeforeFloating(StructuredGraph graph, FloatingNode node) {
         ScheduleResult schedule = graph.getLastSchedule();
         NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
+        Block block = nodeToBlock.get(node);
         BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap();
-
-        Block block = nodeToBlock.get(node);
         FixedWithNextNode result = null;
         for (Node n : blockToNodes.get(block)) {
+            if (n.equals(node)) {
+                break;
+            }
             if (n instanceof FixedWithNextNode) {
                 result = (FixedWithNextNode) n;
             }
-            if (n.equals(node)) {
-                break;
-            }
         }
         assert result != null;
         return result;
     }
 
     /**
+     * Find first dominating {@link FixedWithNextNode} that has a valid state reaching it starting
+     * from the given node.
+     *
+     * @param graph
+     * @param stateMapper
+     * @param node
+     * @return {@link FixedWithNextNode} that we can use as an insertion point
+     */
+    private static FixedWithNextNode findFixedWithValidState(StructuredGraph graph, FrameStateMapperClosure stateMapper, FixedWithNextNode node) {
+        ScheduleResult schedule = graph.getLastSchedule();
+        NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
+        Block block = nodeToBlock.get(node);
+
+        Node n = node;
+        do {
+            if (isFixedWithValidState(stateMapper, n)) {
+                return (FixedWithNextNode) n;
+            }
+            while (n != block.getBeginNode()) {
+                n = n.predecessor();
+                if (isFixedWithValidState(stateMapper, n)) {
+                    return (FixedWithNextNode) n;
+                }
+            }
+            block = block.getDominator();
+            if (block != null) {
+                n = block.getEndNode();
+            }
+        } while (block != null);
+
+        return graph.start();
+    }
+
+    private static boolean isFixedWithValidState(FrameStateMapperClosure stateMapper, Node n) {
+        if (n instanceof FixedWithNextNode) {
+            FixedWithNextNode fixed = (FixedWithNextNode) n;
+            assert stateMapper.getState(fixed) != null;
+            if (!BytecodeFrame.isPlaceholderBci(stateMapper.getState(fixed).bci)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Compute frame states for all fixed nodes in the graph.
+     */
+    private static class FrameStateMapperClosure extends NodeIteratorClosure<FrameState> {
+        private NodeMap<FrameState> reachingStates;
+
+        @Override
+        protected FrameState processNode(FixedNode node, FrameState previousState) {
+            FrameState currentState = previousState;
+            if (node instanceof StateSplit) {
+                StateSplit stateSplit = (StateSplit) node;
+                FrameState stateAfter = stateSplit.stateAfter();
+                if (stateAfter != null) {
+                    currentState = stateAfter;
+                }
+            }
+            reachingStates.put(node, currentState);
+            return currentState;
+        }
+
+        @Override
+        protected FrameState merge(AbstractMergeNode merge, List<FrameState> states) {
+            FrameState singleFrameState = singleFrameState(states);
+            FrameState currentState = singleFrameState == null ? merge.stateAfter() : singleFrameState;
+            reachingStates.put(merge, currentState);
+            return currentState;
+        }
+
+        @Override
+        protected FrameState afterSplit(AbstractBeginNode node, FrameState oldState) {
+            return oldState;
+        }
+
+        @Override
+        protected EconomicMap<LoopExitNode, FrameState> processLoop(LoopBeginNode loop, FrameState initialState) {
+            return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates;
+        }
+
+        private static FrameState singleFrameState(List<FrameState> states) {
+            FrameState singleState = states.get(0);
+            for (int i = 1; i < states.size(); ++i) {
+                if (states.get(i) != singleState) {
+                    return null;
+                }
+            }
+            return singleState;
+        }
+
+        FrameStateMapperClosure(StructuredGraph graph) {
+            reachingStates = new NodeMap<>(graph);
+        }
+
+        public FrameState getState(Node n) {
+            return reachingStates.get(n);
+        }
+
+        public void addState(Node n, FrameState s) {
+            reachingStates.setAndGrow(n, s);
+        }
+    }
+
+    /**
      * Try to find dominating node doing the resolution that can be reused.
      *
      * @param graph
@@ -223,10 +345,11 @@
      * {@link ResolveConstantNode}.
      *
      * @param graph
+     * @param stateMapper
      * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
      *            resolution.
      */
-    private static void replaceWithResolution(StructuredGraph graph, ConstantNode node) {
+    private static void replaceWithResolution(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node) {
         HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
         HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
         ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass();
@@ -249,27 +372,54 @@
             } else {
                 fixedReplacement = graph.add(new ResolveConstantNode(node));
             }
-            graph.addAfterFixed(findFixedWithNextBefore(graph, node), fixedReplacement);
+            insertReplacement(graph, stateMapper, node, fixedReplacement);
             replacement = fixedReplacement;
         }
         node.replaceAtUsages(replacement, n -> !isReplacementNode(n));
     }
 
     /**
+     * Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with indirection.
+     *
+     * @param graph
+     * @param stateMapper
+     * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
+     *            resolution.
+     */
+    private void handleHotSpotMetaspaceConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node) {
+        HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
+        HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
+
+        if (type != null) {
+            if (verifyFingerprints && checkForBadFingerprint(type)) {
+                throw new GraalError("Type with bad fingerprint: " + type);
+            }
+            assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants";
+            tryToReplaceWithExisting(graph, node);
+            if (anyUsagesNeedReplacement(node)) {
+                replaceWithResolution(graph, stateMapper, node);
+            }
+        } else {
+            throw new GraalError("Unsupported metaspace constant type: " + type);
+        }
+    }
+
+    /**
      * Replace an object constant with an indirect load {@link ResolveConstantNode}. Currently we
      * support only strings.
      *
      * @param graph
+     * @param stateMapper
      * @param node {@link ConstantNode} containing a {@link HotSpotObjectConstant} that needs
      *            resolution.
      */
-    private static void handleHotSpotObjectConstant(StructuredGraph graph, ConstantNode node) {
+    private static void handleHotSpotObjectConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node) {
         HotSpotObjectConstant constant = (HotSpotObjectConstant) node.asJavaConstant();
         HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) constant.getType();
         if (type.mirror().equals(String.class)) {
             assert !constant.isCompressed() : "No support for replacing compressed oop constants";
             FixedWithNextNode replacement = graph.add(new ResolveConstantNode(node));
-            graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement);
+            insertReplacement(graph, stateMapper, node, replacement);
             node.replaceAtUsages(replacement, n -> !(n instanceof ResolveConstantNode));
         } else {
             throw new GraalError("Unsupported object constant type: " + type);
@@ -281,16 +431,17 @@
      * {@link ResolveMethodAndLoadCountersNode}, expose a klass constant of the holder.
      *
      * @param graph
+     * @param stateMapper
      * @param node
      * @param context
      */
-    private static void handleLoadMethodCounters(StructuredGraph graph, LoadMethodCountersNode node, PhaseContext context) {
+    private static void handleLoadMethodCounters(StructuredGraph graph, FrameStateMapperClosure stateMapper, LoadMethodCountersNode node, PhaseContext context) {
         ResolvedJavaType type = node.getMethod().getDeclaringClass();
         Stamp hubStamp = context.getStampProvider().createHubStamp((ObjectStamp) StampFactory.objectNonNull());
         ConstantReflectionProvider constantReflection = context.getConstantReflection();
         ConstantNode klassHint = ConstantNode.forConstant(hubStamp, constantReflection.asObjectHub(type), context.getMetaAccess(), graph);
         FixedWithNextNode replacement = graph.add(new ResolveMethodAndLoadCountersNode(node.getMethod(), klassHint));
-        graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement);
+        insertReplacement(graph, stateMapper, node, replacement);
         node.replaceAtUsages(replacement, n -> !(n instanceof ResolveMethodAndLoadCountersNode));
     }
 
@@ -299,13 +450,15 @@
      * klass constants.
      *
      * @param graph
+     * @param stateMapper
      * @param context
      */
-    private static void replaceLoadMethodCounters(StructuredGraph graph, PhaseContext context) {
+    private static void replaceLoadMethodCounters(StructuredGraph graph, FrameStateMapperClosure stateMapper, PhaseContext context) {
         new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false);
+
         for (LoadMethodCountersNode node : getLoadMethodCountersNodes(graph)) {
             if (anyUsagesNeedReplacement(node)) {
-                handleLoadMethodCounters(graph, node, context);
+                handleLoadMethodCounters(graph, stateMapper, node, context);
             }
         }
     }
@@ -314,29 +467,33 @@
      * Replace object and klass constants with resolution nodes or reuse preceding initializations.
      *
      * @param graph
+     * @param stateMapper
      */
-    private void replaceKlassesAndObjects(StructuredGraph graph) {
+    private void replaceKlassesAndObjects(StructuredGraph graph, FrameStateMapperClosure stateMapper) {
         new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false);
 
         for (ConstantNode node : getConstantNodes(graph)) {
             Constant constant = node.asConstant();
             if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) {
-                handleHotSpotMetaspaceConstant(graph, node);
+                handleHotSpotMetaspaceConstant(graph, stateMapper, node);
             } else if (constant instanceof HotSpotObjectConstant && anyUsagesNeedReplacement(node)) {
-                handleHotSpotObjectConstant(graph, node);
+                handleHotSpotObjectConstant(graph, stateMapper, node);
             }
         }
     }
 
     @Override
     protected void run(StructuredGraph graph, PhaseContext context) {
+        FrameStateMapperClosure stateMapper = new FrameStateMapperClosure(graph);
+        ReentrantNodeIterator.apply(stateMapper, graph.start(), null);
+
         // Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass
         // constants.
-        replaceLoadMethodCounters(graph, context);
+        replaceLoadMethodCounters(graph, stateMapper, context);
 
         // Replace object and klass constants (including the ones added in the previous pass) with
         // resolution nodes.
-        replaceKlassesAndObjects(graph);
+        replaceKlassesAndObjects(graph, stateMapper);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/AssertionSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/AssertionSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -75,8 +76,8 @@
         private final SnippetInfo assertion = snippet(AssertionSnippets.class, "assertion");
         private final SnippetInfo stubAssertion = snippet(AssertionSnippets.class, "stubAssertion");
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(AssertionNode assertionNode, LoweringTool tool) {
@@ -85,7 +86,7 @@
             args.add("condition", assertionNode.condition());
             args.addConst("message", "failed runtime assertion in snippet/stub: " + assertionNode.message() + " (" + graph.method() + ")");
 
-            template(args).instantiate(providers.getMetaAccess(), assertionNode, DEFAULT_REPLACER, args);
+            template(assertionNode.getDebug(), args).instantiate(providers.getMetaAccess(), assertionNode, DEFAULT_REPLACER, args);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HashCodeSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HashCodeSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,6 +36,7 @@
 import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
 
 import org.graalvm.compiler.api.replacements.Snippet;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -78,15 +79,15 @@
 
         private final SnippetInfo identityHashCodeSnippet = snippet(HashCodeSnippets.class, "identityHashCodeSnippet", HotSpotReplacementsUtil.MARK_WORD_LOCATION);
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(IdentityHashCodeNode node, LoweringTool tool) {
             StructuredGraph graph = node.graph();
             Arguments args = new Arguments(identityHashCodeSnippet, graph.getGuardsStage(), tool.getLoweringStage());
             args.add("thisObj", node.object);
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(node.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -44,6 +44,7 @@
 import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
@@ -259,8 +260,8 @@
 
         private final Counters counters;
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.counters = new Counters(factory);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/LoadExceptionObjectSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/LoadExceptionObjectSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,6 +36,7 @@
 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.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
 import org.graalvm.compiler.hotspot.word.HotSpotWordTypes;
@@ -82,8 +83,8 @@
         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);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.wordTypes = providers.getWordTypes();
         }
 
@@ -100,7 +101,7 @@
             } else {
                 Arguments args = new Arguments(loadException, loadExceptionObject.graph().getGuardsStage(), tool.getLoweringStage());
                 args.addConst("threadRegister", registers.getThreadRegister());
-                template(args).instantiate(providers.getMetaAccess(), loadExceptionObject, DEFAULT_REPLACER, args);
+                template(loadExceptionObject.getDebug(), args).instantiate(providers.getMetaAccess(), loadExceptionObject, DEFAULT_REPLACER, args);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -80,6 +80,7 @@
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
@@ -723,8 +724,9 @@
         private final boolean useFastLocking;
         public final Counters counters;
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target, boolean useFastLocking) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target,
+                        boolean useFastLocking) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.useFastLocking = useFastLocking;
 
             this.counters = new Counters(factory);
@@ -756,7 +758,7 @@
                 args.addConst("counters", counters);
             }
 
-            template(args).instantiate(providers.getMetaAccess(), monitorenterNode, DEFAULT_REPLACER, args);
+            template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), monitorenterNode, DEFAULT_REPLACER, args);
         }
 
         public void lower(MonitorExitNode monitorexitNode, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -775,7 +777,7 @@
             args.addConst("options", graph.getOptions());
             args.addConst("counters", counters);
 
-            template(args).instantiate(providers.getMetaAccess(), monitorexitNode, DEFAULT_REPLACER, args);
+            template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), monitorexitNode, DEFAULT_REPLACER, args);
         }
 
         public static boolean isTracingEnabledForType(ValueNode object) {
@@ -843,7 +845,7 @@
 
                         Arguments args = new Arguments(checkCounter, graph.getGuardsStage(), tool.getLoweringStage());
                         args.addConst("errMsg", msg);
-                        inlineeGraph = template(args).copySpecializedGraph();
+                        inlineeGraph = template(graph.getDebug(), args).copySpecializedGraph(graph.getDebug());
                         InliningUtil.inline(invoke, inlineeGraph, false, null);
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -74,7 +74,7 @@
 import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
@@ -591,8 +591,9 @@
         private final GraalHotSpotVMConfig config;
         private final Counters counters;
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target, GraalHotSpotVMConfig config) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target,
+                        GraalHotSpotVMConfig config) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.config = config;
             counters = new Counters(factory);
         }
@@ -620,8 +621,8 @@
             args.addConst("options", localOptions);
             args.addConst("counters", counters);
 
-            SnippetTemplate template = template(args);
-            Debug.log("Lowering allocateInstance in %s: node=%s, template=%s, arguments=%s", graph, newInstanceNode, template, args);
+            SnippetTemplate template = template(graph.getDebug(), args);
+            graph.getDebug().log("Lowering allocateInstance in %s: node=%s, template=%s, arguments=%s", graph, newInstanceNode, template, args);
             template.instantiate(providers.getMetaAccess(), newInstanceNode, DEFAULT_REPLACER, args);
         }
 
@@ -663,8 +664,8 @@
             args.addConst("typeContext", ProfileAllocations.getValue(localOptions) ? arrayType.toJavaName(false) : "");
             args.addConst("options", localOptions);
             args.addConst("counters", counters);
-            SnippetTemplate template = template(args);
-            Debug.log("Lowering allocateArray in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, args);
+            SnippetTemplate template = template(graph.getDebug(), args);
+            graph.getDebug().log("Lowering allocateArray in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, args);
             template.instantiate(providers.getMetaAccess(), newArrayNode, DEFAULT_REPLACER, args);
         }
 
@@ -680,7 +681,7 @@
             args.addConst("options", localOptions);
             args.addConst("counters", counters);
 
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(newInstanceNode.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), newInstanceNode, DEFAULT_REPLACER, args);
         }
 
@@ -709,7 +710,7 @@
             args.add("prototypeMarkWord", lookupArrayClass(tool, JavaKind.Object).prototypeMarkWord());
             args.addConst("options", localOptions);
             args.addConst("counters", counters);
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(graph.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), newArrayNode, DEFAULT_REPLACER, args);
         }
 
@@ -733,7 +734,7 @@
             args.add("hub", hub);
             args.addConst("rank", rank);
             args.addVarargs("dimensions", int.class, StampFactory.forKind(JavaKind.Int), dims);
-            template(args).instantiate(providers.getMetaAccess(), newmultiarrayNode, DEFAULT_REPLACER, args);
+            template(newmultiarrayNode.getDebug(), args).instantiate(providers.getMetaAccess(), newmultiarrayNode, DEFAULT_REPLACER, args);
         }
 
         private static int instanceSize(HotSpotResolvedObjectType type) {
@@ -747,7 +748,7 @@
                 Arguments args = new Arguments(verifyHeap, verifyHeapNode.graph().getGuardsStage(), tool.getLoweringStage());
                 args.addConst("threadRegister", registers.getThreadRegister());
 
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(verifyHeapNode.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), verifyHeapNode, DEFAULT_REPLACER, args);
             } else {
                 GraphUtil.removeFixedWithUnusedInputs(verifyHeapNode);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,7 @@
 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampPair;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
@@ -84,22 +83,23 @@
                     final ResolvedJavaMethod snippetMethod = tool.getMetaAccess().lookupJavaMethod(method);
                     final Replacements replacements = tool.getReplacements();
                     StructuredGraph snippetGraph = null;
-                    try (Scope s = Debug.scope("ArrayCloneSnippet", snippetMethod)) {
+                    DebugContext debug = getDebug();
+                    try (DebugContext.Scope s = debug.scope("ArrayCloneSnippet", snippetMethod)) {
                         snippetGraph = replacements.getSnippet(snippetMethod, null);
                     } catch (Throwable e) {
-                        throw Debug.handle(e);
+                        throw debug.handle(e);
                     }
 
                     assert snippetGraph != null : "ObjectCloneSnippets should be installed";
                     assert getConcreteType(stamp()) != null;
-                    return lowerReplacement((StructuredGraph) snippetGraph.copy(), tool);
+                    return lowerReplacement((StructuredGraph) snippetGraph.copy(getDebug()), tool);
                 }
                 assert false : "unhandled array type " + type.getComponentType().getJavaKind();
             } else {
                 Assumptions assumptions = graph().getAssumptions();
                 type = getConcreteType(getObject().stamp());
                 if (type != null) {
-                    StructuredGraph newGraph = new StructuredGraph.Builder(graph().getOptions(), AllowAssumptions.ifNonNull(assumptions)).build();
+                    StructuredGraph newGraph = new StructuredGraph.Builder(graph().getOptions(), graph().getDebug(), AllowAssumptions.ifNonNull(assumptions)).build();
                     ParameterNode param = newGraph.addWithoutUnique(new ParameterNode(0, StampPair.createSingle(getObject().stamp())));
                     NewInstanceNode newInstance = newGraph.add(new NewInstanceNode(type, true));
                     newGraph.addAfterFixed(newGraph.start(), newInstance);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/StringToBytesSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/StringToBytesSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
 import org.graalvm.compiler.nodes.debug.StringToBytesNode;
@@ -74,15 +75,15 @@
 
         private final SnippetInfo create;
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             create = snippet(StringToBytesSnippets.class, "transform", NamedLocationIdentity.getArrayLocation(JavaKind.Byte));
         }
 
         public void lower(StringToBytesNode stringToBytesNode, LoweringTool tool) {
             Arguments args = new Arguments(create, stringToBytesNode.graph().getGuardsStage(), tool.getLoweringStage());
             args.addConst("compilationTimeString", stringToBytesNode.getValue());
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(stringToBytesNode.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), stringToBytesNode, DEFAULT_REPLACER, args);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/UnsafeLoadSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/UnsafeLoadSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,6 +26,7 @@
 import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
 
 import org.graalvm.compiler.api.replacements.Snippet;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.nodes.extended.FixedValueAnchorNode;
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
@@ -56,15 +57,15 @@
 
         private final SnippetInfo unsafeLoad = snippet(UnsafeLoadSnippets.class, "lowerUnsafeLoad");
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(RawLoadNode load, LoweringTool tool) {
             Arguments args = new Arguments(unsafeLoad, load.graph().getGuardsStage(), tool.getLoweringStage());
             args.add("object", load.object());
             args.add("offset", load.offset());
-            template(args).instantiate(providers.getMetaAccess(), load, DEFAULT_REPLACER, args);
+            template(load.getDebug(), args).instantiate(providers.getMetaAccess(), load, DEFAULT_REPLACER, args);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -49,6 +49,7 @@
 import org.graalvm.compiler.core.common.CompressEncoding;
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -406,8 +407,9 @@
         private final CompressEncoding oopEncoding;
         private final Counters counters;
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target, CompressEncoding oopEncoding) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target,
+                        CompressEncoding oopEncoding) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.oopEncoding = oopEncoding;
             this.counters = new Counters(factory);
         }
@@ -423,7 +425,7 @@
                 args.add("object", address.getBase());
             }
             args.addConst("counters", counters);
-            template(args).instantiate(providers.getMetaAccess(), writeBarrier, DEFAULT_REPLACER, args);
+            template(writeBarrier.getDebug(), args).instantiate(providers.getMetaAccess(), writeBarrier, DEFAULT_REPLACER, args);
         }
 
         public void lower(SerialArrayRangeWriteBarrier arrayRangeWriteBarrier, LoweringTool tool) {
@@ -431,7 +433,7 @@
             args.add("object", arrayRangeWriteBarrier.getObject());
             args.add("startIndex", arrayRangeWriteBarrier.getStartIndex());
             args.add("length", arrayRangeWriteBarrier.getLength());
-            template(args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
+            template(arrayRangeWriteBarrier.getDebug(), args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
         }
 
         public void lower(G1PreWriteBarrier writeBarrierPre, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -456,7 +458,7 @@
             args.addConst("threadRegister", registers.getThreadRegister());
             args.addConst("trace", traceBarrier(writeBarrierPre.graph()));
             args.addConst("counters", counters);
-            template(args).instantiate(providers.getMetaAccess(), writeBarrierPre, DEFAULT_REPLACER, args);
+            template(writeBarrierPre.getDebug(), args).instantiate(providers.getMetaAccess(), writeBarrierPre, DEFAULT_REPLACER, args);
         }
 
         public void lower(G1ReferentFieldReadBarrier readBarrier, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -481,7 +483,7 @@
             args.addConst("threadRegister", registers.getThreadRegister());
             args.addConst("trace", traceBarrier(readBarrier.graph()));
             args.addConst("counters", counters);
-            template(args).instantiate(providers.getMetaAccess(), readBarrier, DEFAULT_REPLACER, args);
+            template(readBarrier.getDebug(), args).instantiate(providers.getMetaAccess(), readBarrier, DEFAULT_REPLACER, args);
         }
 
         public void lower(G1PostWriteBarrier writeBarrierPost, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -511,7 +513,7 @@
             args.addConst("threadRegister", registers.getThreadRegister());
             args.addConst("trace", traceBarrier(writeBarrierPost.graph()));
             args.addConst("counters", counters);
-            template(args).instantiate(providers.getMetaAccess(), writeBarrierPost, DEFAULT_REPLACER, args);
+            template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), writeBarrierPost, DEFAULT_REPLACER, args);
         }
 
         public void lower(G1ArrayRangePreWriteBarrier arrayRangeWriteBarrier, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -520,7 +522,7 @@
             args.add("startIndex", arrayRangeWriteBarrier.getStartIndex());
             args.add("length", arrayRangeWriteBarrier.getLength());
             args.addConst("threadRegister", registers.getThreadRegister());
-            template(args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
+            template(arrayRangeWriteBarrier.getDebug(), args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
         }
 
         public void lower(G1ArrayRangePostWriteBarrier arrayRangeWriteBarrier, HotSpotRegistersProvider registers, LoweringTool tool) {
@@ -529,7 +531,7 @@
             args.add("startIndex", arrayRangeWriteBarrier.getStartIndex());
             args.add("length", arrayRangeWriteBarrier.getLength());
             args.addConst("threadRegister", registers.getThreadRegister());
-            template(args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
+            template(arrayRangeWriteBarrier.getDebug(), args).instantiate(providers.getMetaAccess(), arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/aot/ResolveConstantSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/aot/ResolveConstantSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
 
 import org.graalvm.compiler.api.replacements.Snippet;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -114,8 +115,8 @@
         private final SnippetInfo initializeKlass = snippet(ResolveConstantSnippets.class, "initializeKlass");
         private final SnippetInfo pureInitializeKlass = snippet(ResolveConstantSnippets.class, "pureInitializeKlass");
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(ResolveConstantNode resolveConstantNode, LoweringTool tool) {
@@ -148,7 +149,7 @@
             Arguments args = new Arguments(snippet, graph.getGuardsStage(), tool.getLoweringStage());
             args.add("constant", value);
 
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(graph.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), resolveConstantNode, DEFAULT_REPLACER, args);
 
             assert resolveConstantNode.hasNoUsages();
@@ -168,7 +169,7 @@
                 Arguments args = new Arguments(initializeKlass, graph.getGuardsStage(), tool.getLoweringStage());
                 args.add("constant", value);
 
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(graph.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), initializeKlassNode, DEFAULT_REPLACER, args);
                 assert initializeKlassNode.hasNoUsages();
                 if (!initializeKlassNode.isDeleted()) {
@@ -186,7 +187,7 @@
             Arguments args = new Arguments(resolveMethodAndLoadCounters, graph.getGuardsStage(), tool.getLoweringStage());
             args.add("method", method);
             args.add("klassHint", resolveMethodAndLoadCountersNode.getHub());
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(graph.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), resolveMethodAndLoadCountersNode, DEFAULT_REPLACER, args);
 
             assert resolveMethodAndLoadCountersNode.hasNoUsages();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,6 +43,7 @@
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -411,8 +412,8 @@
 
     public static class Templates extends SnippetTemplate.AbstractTemplates {
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
             this.counters = new Counters(factory);
         }
 
@@ -617,7 +618,7 @@
             args.add("destPos", arraycopy.getDestinationPosition());
             args.addConst("length", arraycopy.getUnrollLength());
             args.addConst("elementKind", arraycopy.getElementKind());
-            template(args).instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
+            template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
         }
 
         /**
@@ -629,7 +630,7 @@
          */
         private void instantiate(Arguments args, BasicArrayCopyNode arraycopy) {
             StructuredGraph graph = arraycopy.graph();
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(graph.getDebug(), args);
             UnmodifiableEconomicMap<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args, false);
             for (Node originalNode : replacements.getKeys()) {
                 if (originalNode instanceof Invoke) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.hotspot.replacements.arraycopy;
 
+import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
 import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayBaseOffset;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayIndexScale;
@@ -31,32 +32,33 @@
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperLog2ElementSizeShift;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.runtime;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize;
-import static org.graalvm.compiler.nodes.NamedLocationIdentity.any;
 import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY;
 import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
 import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
-import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
+import static org.graalvm.word.LocationIdentity.any;
 
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.core.common.NumUtil;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
-import org.graalvm.compiler.nodes.extended.UnsafeCopyNode;
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.extended.RawStoreNode;
+import org.graalvm.compiler.nodes.extended.UnsafeCopyNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.replacements.SnippetTemplate;
 import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates;
 import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
 import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
+import org.graalvm.compiler.replacements.Snippets;
 import org.graalvm.compiler.word.ObjectAccess;
 import org.graalvm.word.LocationIdentity;
 import org.graalvm.word.Unsigned;
 import org.graalvm.word.WordFactory;
-import org.graalvm.compiler.replacements.Snippets;
+
 import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.meta.JavaKind;
 
@@ -284,8 +286,8 @@
         private final SnippetInfo[] arraycopySnippets;
         private final SnippetInfo genericPrimitiveSnippet;
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
 
             arraycopySnippets = new SnippetInfo[JavaKind.values().length];
             arraycopySnippets[JavaKind.Boolean.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyBoolean");
@@ -315,7 +317,7 @@
             Arguments args = new Arguments(snippet, node.graph().getGuardsStage(), tool.getLoweringStage());
             node.addSnippetArguments(args);
 
-            SnippetTemplate template = template(args);
+            SnippetTemplate template = template(node.getDebug(), args);
             template.instantiate(providers.getMetaAccess(), node, DEFAULT_REPLACER, args);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,6 +31,7 @@
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
@@ -114,8 +115,8 @@
         private final SnippetInfo profileBackedgeWithProbability = snippet(ProbabilisticProfileSnippets.class, "profileBackedgeWithProbability");
         private final SnippetInfo profileConditionalBackedgeWithProbability = snippet(ProbabilisticProfileSnippets.class, "profileConditionalBackedgeWithProbability");
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(ProfileNode profileNode, LoweringTool tool) {
@@ -141,7 +142,7 @@
                 args.add("bci", bci);
                 args.add("targetBci", targetBci);
 
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(graph.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), profileNode, DEFAULT_REPLACER, args);
             } else if (profileNode instanceof ProfileInvokeNode) {
                 ProfileInvokeNode profileInvokeNode = (ProfileInvokeNode) profileNode;
@@ -151,7 +152,7 @@
                 args.add("random", profileInvokeNode.getRandom());
                 args.addConst("freqLog", profileInvokeNode.getNotificationFreqLog());
                 args.addConst("probLog", profileInvokeNode.getProbabilityLog());
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(graph.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), profileNode, DEFAULT_REPLACER, args);
             } else {
                 throw new GraalError("Unsupported profile node type: " + profileNode);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,6 +31,7 @@
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
@@ -96,8 +97,8 @@
         private final SnippetInfo profileBackedge = snippet(ProfileSnippets.class, "profileBackedge");
         private final SnippetInfo profileConditionalBackedge = snippet(ProfileSnippets.class, "profileConditionalBackedge");
 
-        public Templates(OptionValues options, HotSpotProviders providers, TargetDescription target) {
-            super(options, providers, providers.getSnippetReflection(), target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
+            super(options, factories, providers, providers.getSnippetReflection(), target);
         }
 
         public void lower(ProfileNode profileNode, LoweringTool tool) {
@@ -119,7 +120,7 @@
                 args.add("bci", bci);
                 args.add("targetBci", targetBci);
 
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(graph.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), profileNode, DEFAULT_REPLACER, args);
             } else if (profileNode instanceof ProfileInvokeNode) {
                 ProfileInvokeNode profileInvokeNode = (ProfileInvokeNode) profileNode;
@@ -127,7 +128,7 @@
                 Arguments args = new Arguments(profileMethodEntry, graph.getGuardsStage(), tool.getLoweringStage());
                 args.add("counters", counters);
                 args.addConst("freqLog", profileInvokeNode.getNotificationFreqLog());
-                SnippetTemplate template = template(args);
+                SnippetTemplate template = template(graph.getDebug(), args);
                 template.instantiate(providers.getMetaAccess(), profileNode, DEFAULT_REPLACER, args);
             } else {
                 throw new GraalError("Unsupported profile node type: " + profileNode);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/CreateExceptionStub.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/CreateExceptionStub.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,20 +22,20 @@
  */
 package org.graalvm.compiler.hotspot.stubs;
 
+import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.NativeCall;
 import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS;
 import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.Transition.SAFEPOINT;
 import static org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProviderImpl.REEXECUTABLE;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.clearPendingException;
 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord;
 import static org.graalvm.word.LocationIdentity.any;
-import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.NativeCall;
 
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
-import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProviderImpl;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,11 @@
  */
 package org.graalvm.compiler.hotspot.stubs;
 
-import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS;
-import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS;
 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCall;
 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCallee;
 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.NativeCall;
+import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS;
+import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS;
 
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.LIRKind;
@@ -34,7 +34,7 @@
 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;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.JavaMethodContext;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.Transition;
@@ -104,7 +104,8 @@
      */
     public ForeignCallStub(OptionValues options, HotSpotJVMCIRuntimeProvider runtime, HotSpotProviders providers, long address, ForeignCallDescriptor descriptor, boolean prependThread,
                     Transition transition,
-                    boolean reexecutable, LocationIdentity... killedLocations) {
+                    boolean reexecutable,
+                    LocationIdentity... killedLocations) {
         super(options, providers, HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getWordTypes(), providers.getForeignCalls(), descriptor, 0L,
                         PRESERVES_REGISTERS, JavaCall, JavaCallee, transition, reexecutable, killedLocations));
         this.jvmciRuntime = runtime;
@@ -223,12 +224,11 @@
      * %r15 on AMD64) and is only prepended if {@link #prependThread} is true.
      */
     @Override
-    protected StructuredGraph getGraph(CompilationIdentifier compilationId) {
+    protected StructuredGraph getGraph(DebugContext debug, CompilationIdentifier compilationId) {
         WordTypes wordTypes = providers.getWordTypes();
         Class<?>[] args = linkage.getDescriptor().getArgumentTypes();
         boolean isObjectResult = !LIRKind.isValue(linkage.getOutgoingCallingConvention().getReturn());
-
-        StructuredGraph graph = new StructuredGraph.Builder(options).name(toString()).compilationId(compilationId).build();
+        StructuredGraph graph = new StructuredGraph.Builder(options, debug).name(toString()).compilationId(compilationId).build();
         graph.disableUnsafeAccessTracking();
 
         GraphKit kit = new GraphKit(graph, providers, wordTypes, providers.getGraphBuilderPlugins());
@@ -243,13 +243,13 @@
         }
         kit.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));
 
-        Debug.dump(Debug.VERBOSE_LEVEL, graph, "Initial stub graph");
+        debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Initial stub graph");
 
         kit.inlineInvokes();
 
         new RemoveValueProxyPhase().apply(graph);
 
-        Debug.dump(Debug.VERBOSE_LEVEL, graph, "Stub graph before compilation");
+        debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Stub graph before compilation");
 
         return graph;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,8 +33,7 @@
 import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -96,7 +95,7 @@
 
     @Override
     @SuppressWarnings("try")
-    protected StructuredGraph getGraph(CompilationIdentifier compilationId) {
+    protected StructuredGraph getGraph(DebugContext debug, CompilationIdentifier compilationId) {
         Plugins defaultPlugins = providers.getGraphBuilderPlugins();
         MetaAccessProvider metaAccess = providers.getMetaAccess();
         SnippetReflectionProvider snippetReflection = providers.getSnippetReflection();
@@ -107,8 +106,8 @@
 
         // Stubs cannot have optimistic assumptions since they have
         // to be valid for the entire run of the VM.
-        final StructuredGraph graph = new StructuredGraph.Builder(options).method(method).compilationId(compilationId).build();
-        try (Scope outer = Debug.scope("SnippetStub", graph)) {
+        final StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).compilationId(compilationId).build();
+        try (DebugContext.Scope outer = debug.scope("SnippetStub", graph)) {
             graph.disableUnsafeAccessTracking();
 
             IntrinsicContext initialIntrinsicContext = new IntrinsicContext(method, method, getReplacementsBytecodeProvider(), INLINE_AFTER_PARSING);
@@ -132,7 +131,7 @@
             canonicalizer.apply(graph, context);
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
 
         return graph;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/Stub.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/Stub.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,22 +22,23 @@
  */
 package org.graalvm.compiler.hotspot.stubs;
 
+import static java.util.Collections.singletonList;
 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.debug.DebugContext.DEFAULT_LOG_STREAM;
+import static org.graalvm.compiler.debug.DebugOptions.DebugStubsAndSnippets;
 import static org.graalvm.compiler.hotspot.HotSpotHostBackend.UNCOMMON_TRAP_HANDLER;
 import static org.graalvm.util.CollectionsUtil.allMatch;
 
 import java.util.ListIterator;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 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.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Description;
 import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
@@ -52,6 +53,7 @@
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.tiers.Suites;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
 import org.graalvm.util.EconomicSet;
 
 import jdk.vm.ci.code.CodeCacheProvider;
@@ -155,7 +157,7 @@
      *
      * @param compilationId unique compilation id for the stub
      */
-    protected abstract StructuredGraph getGraph(CompilationIdentifier compilationId);
+    protected abstract StructuredGraph getGraph(DebugContext debug, CompilationIdentifier compilationId);
 
     @Override
     public String toString() {
@@ -172,39 +174,50 @@
      */
     protected abstract Object debugScopeContext();
 
+    private static final AtomicInteger nextStubId = new AtomicInteger();
+
+    private DebugContext openDebugContext(DebugContext outer) {
+        if (DebugStubsAndSnippets.getValue(options)) {
+            Description description = new Description(linkage, "Stub_" + nextStubId.incrementAndGet());
+            return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, singletonList(new GraalDebugHandlersFactory(providers.getSnippetReflection())));
+        }
+        return DebugContext.DISABLED;
+    }
+
     /**
      * Gets the code for this stub, compiling it first if necessary.
      */
     @SuppressWarnings("try")
     public synchronized InstalledCode getCode(final Backend backend) {
         if (code == null) {
-            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);
-                try (Scope s = Debug.scope("CodeInstall", compResult)) {
-                    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(codeCache, null, null, compResult);
-                    code = codeCache.installCode(null, compiledCode, null, null, false);
+            try (DebugContext debug = openDebugContext(DebugContext.forCurrentThread())) {
+                try (DebugContext.Scope d = debug.scope("CompilingStub", providers.getCodeCache(), debugScopeContext())) {
+                    CodeCacheProvider codeCache = providers.getCodeCache();
+                    CompilationResult compResult = buildCompilationResult(debug, backend);
+                    try (DebugContext.Scope s = debug.scope("CodeInstall", compResult);
+                                    DebugContext.Activation a = debug.activate()) {
+                        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(codeCache, null, null, compResult);
+                        code = codeCache.installCode(null, compiledCode, null, null, false);
+                    } catch (Throwable e) {
+                        throw debug.handle(e);
+                    }
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
-            } catch (Throwable e) {
-                throw Debug.handle(e);
+                assert code != null : "error installing stub " + this;
             }
-            assert code != null : "error installing stub " + this;
         }
 
         return code;
     }
 
     @SuppressWarnings("try")
-    private CompilationResult buildCompilationResult(final Backend backend) {
+    private CompilationResult buildCompilationResult(DebugContext debug, final Backend backend) {
         CompilationResult compResult = new CompilationResult(toString(), GeneratePIC.getValue(options));
-        final StructuredGraph graph = getGraph(getStubCompilationId());
+        final StructuredGraph graph = getGraph(debug, getStubCompilationId());
 
         // Stubs cannot be recompiled so they cannot be compiled with assumptions
         assert graph.getAssumptions() == null;
@@ -215,14 +228,14 @@
             graph.replaceFixed(graph.start(), newStart);
         }
 
-        try (Scope s0 = Debug.scope("StubCompilation", graph, providers.getCodeCache())) {
+        try (DebugContext.Scope s0 = debug.scope("StubCompilation", graph, providers.getCodeCache())) {
             Suites suites = createSuites();
             emitFrontEnd(providers, backend, graph, providers.getSuites().getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, DefaultProfilingInfo.get(TriState.UNKNOWN), suites);
             LIRSuites lirSuites = createLIRSuites();
             emitBackEnd(graph, Stub.this, getInstalledCodeOwner(), backend, compResult, CompilationResultBuilderFactory.Default, getRegisterConfig(), lirSuites);
             assert checkStubInvariants(compResult);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
         return compResult;
     }
@@ -231,11 +244,11 @@
      * Gets a {@link CompilationResult} that can be used for code generation. Required for AOT.
      */
     @SuppressWarnings("try")
-    public CompilationResult getCompilationResult(final Backend backend) {
-        try (Scope d = Debug.sandbox("CompilingStub", DebugScope.getConfig(), providers.getCodeCache(), debugScopeContext())) {
-            return buildCompilationResult(backend);
+    public CompilationResult getCompilationResult(DebugContext debug, final Backend backend) {
+        try (DebugContext.Scope d = debug.scope("CompilingStub", providers.getCodeCache(), debugScopeContext())) {
+            return buildCompilationResult(debug, backend);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java	Fri Jul 07 09:40:47 2017 -0700
@@ -93,7 +93,7 @@
 import org.graalvm.compiler.bytecode.BytecodeTableSwitch;
 import org.graalvm.compiler.bytecode.Bytecodes;
 import org.graalvm.compiler.core.common.PermanentBailoutException;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
@@ -437,12 +437,14 @@
     private static final int LOOP_HEADER_INITIAL_CAPACITY = 4;
 
     private int blocksNotYetAssignedId;
+    private final DebugContext debug;
 
     /**
      * Creates a new BlockMap instance from {@code code}.
      */
-    private BciBlockMapping(Bytecode code) {
+    private BciBlockMapping(Bytecode code, DebugContext debug) {
         this.code = code;
+        this.debug = debug;
         this.exceptionHandlers = code.getExceptionHandlers();
     }
 
@@ -464,7 +466,7 @@
             }
             createJsrAlternatives(blockMap, blockMap[0]);
         }
-        if (Debug.isLogEnabled()) {
+        if (debug.isLogEnabled()) {
             this.log(blockMap, "Before BlockOrder");
         }
         computeBlockOrder(blockMap);
@@ -473,7 +475,7 @@
         assert verify();
 
         startBlock = blockMap[0];
-        if (Debug.isLogEnabled()) {
+        if (debug.isLogEnabled()) {
             this.log(blockMap, "Before LivenessAnalysis");
         }
     }
@@ -703,7 +705,7 @@
             block.addSuccessor(block.getRetSuccessor());
             assert block.getRetSuccessor() != block.getJsrSuccessor();
         }
-        Debug.log("JSR alternatives block %s  sux %s  jsrSux %s  retSux %s  jsrScope %s", block, block.getSuccessors(), block.getJsrSuccessor(), block.getRetSuccessor(), block.getJsrScope());
+        debug.log("JSR alternatives block %s  sux %s  jsrSux %s  retSux %s  jsrScope %s", block, block.getSuccessors(), block.getJsrSuccessor(), block.getRetSuccessor(), block.getJsrScope());
 
         if (block.getJsrSuccessor() != null || !scope.isEmpty()) {
             for (int i = 0; i < block.getSuccessorCount(); i++) {
@@ -870,9 +872,9 @@
     }
 
     public void log(BciBlock[] blockMap, String name) {
-        if (Debug.isLogEnabled()) {
+        if (debug.isLogEnabled()) {
             String n = System.lineSeparator();
-            StringBuilder sb = new StringBuilder(Debug.currentScope()).append("BlockMap ").append(name).append(" :");
+            StringBuilder sb = new StringBuilder(debug.getCurrentScopeName()).append("BlockMap ").append(name).append(" :");
             sb.append(n);
             Iterable<BciBlock> it;
             if (blocks == null) {
@@ -905,7 +907,7 @@
                 }
                 sb.append(n);
             }
-            Debug.log("%s", sb);
+            debug.log("%s", sb);
         }
     }
 
@@ -946,7 +948,7 @@
 
             assert block.loops == 0;
             block.loops = 1L << nextLoop;
-            Debug.log("makeLoopHeader(%s) -> %x", block, block.loops);
+            debug.log("makeLoopHeader(%s) -> %x", block, block.loops);
             if (loopHeaders == null) {
                 loopHeaders = new BciBlock[LOOP_HEADER_INITIAL_CAPACITY];
             } else if (nextLoop >= loopHeaders.length) {
@@ -992,7 +994,7 @@
         }
 
         block.loops = loops;
-        Debug.log("computeBlockOrder(%s) -> %x", block, block.loops);
+        debug.log("computeBlockOrder(%s) -> %x", block, block.loops);
 
         if (block.isLoopHeader) {
             loops &= ~(1L << block.loopId);
@@ -1024,7 +1026,7 @@
         if (block.loops != loops) {
             loopChanges = true;
             block.loops = loops;
-            Debug.log("fixLoopBits0(%s) -> %x", block, block.loops);
+            debug.log("fixLoopBits0(%s) -> %x", block, block.loops);
         }
 
         if (block.isLoopHeader) {
@@ -1034,11 +1036,11 @@
         return loops;
     }
 
-    public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options) {
-        BciBlockMapping map = new BciBlockMapping(code);
+    public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options, DebugContext debug) {
+        BciBlockMapping map = new BciBlockMapping(code, debug);
         map.build(stream, options);
-        if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
-            Debug.dump(Debug.INFO_LEVEL, map, code.getMethod().format("After block building %f %R %H.%n(%P)"));
+        if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
+            debug.dump(DebugContext.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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,8 @@
 import static java.lang.reflect.Modifier.SYNCHRONIZED;
 import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateRecompile;
 import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile;
+import static jdk.vm.ci.meta.DeoptimizationAction.None;
+import static jdk.vm.ci.meta.DeoptimizationReason.ClassCastException;
 import static jdk.vm.ci.meta.DeoptimizationReason.JavaSubroutineMismatch;
 import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException;
 import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint;
@@ -286,10 +288,9 @@
 import org.graalvm.compiler.core.common.type.TypeReference;
 import org.graalvm.compiler.core.common.util.Util;
 import org.graalvm.compiler.debug.Assertions;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.debug.TTY;
@@ -452,9 +453,9 @@
     /**
      * Meters the number of actual bytecodes parsed.
      */
-    public static final DebugCounter BytecodesParsed = Debug.counter("BytecodesParsed");
-
-    protected static final DebugCounter EXPLICIT_EXCEPTIONS = Debug.counter("ExplicitExceptions");
+    public static final CounterKey BytecodesParsed = DebugContext.counter("BytecodesParsed");
+
+    protected static final CounterKey EXPLICIT_EXCEPTIONS = DebugContext.counter("ExplicitExceptions");
 
     /**
      * A scoped object for tasks to be performed after parsing an intrinsic such as processing
@@ -629,6 +630,7 @@
     private final GraphBuilderPhase.Instance graphBuilderInstance;
     protected final StructuredGraph graph;
     protected final OptionValues options;
+    protected final DebugContext debug;
 
     private BciBlockMapping blockMap;
     private LocalLiveness liveness;
@@ -663,6 +665,7 @@
         this.graphBuilderInstance = graphBuilderInstance;
         this.graph = graph;
         this.options = graph.getOptions();
+        this.debug = graph.getDebug();
         this.graphBuilderConfig = graphBuilderInstance.graphBuilderConfig;
         this.optimisticOpts = graphBuilderInstance.optimisticOpts;
         this.metaAccess = graphBuilderInstance.metaAccess;
@@ -716,7 +719,7 @@
             TTY.println(Util.indent(profilingInfo.toString(method, CodeUtil.NEW_LINE), "  "));
         }
 
-        try (Indent indent = Debug.logAndIndent("build graph for %s", method)) {
+        try (Indent indent = debug.logAndIndent("build graph for %s", method)) {
             if (bytecodeProvider.shouldRecordMethodDependencies()) {
                 assert getParent() != null || method.equals(graph.method());
                 // Record method dependency in the graph
@@ -724,7 +727,7 @@
             }
 
             // compute the block map, setup exception handlers and get the entrypoint(s)
-            BciBlockMapping newMapping = BciBlockMapping.create(stream, code, options);
+            BciBlockMapping newMapping = BciBlockMapping.create(stream, code, options, graph.getDebug());
             this.blockMap = newMapping;
             this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()];
             this.entryStateArray = new FrameStateBuilder[blockMap.getBlockCount()];
@@ -738,11 +741,11 @@
              */
             assert computeKindVerification(startFrameState);
 
-            try (Scope s = Debug.scope("LivenessAnalysis")) {
+            try (DebugContext.Scope s = debug.scope("LivenessAnalysis")) {
                 int maxLocals = method.getMaxLocals();
-                liveness = LocalLiveness.compute(stream, blockMap.getBlocks(), maxLocals, blockMap.getLoopCount());
+                liveness = LocalLiveness.compute(debug, stream, blockMap.getBlocks(), maxLocals, blockMap.getLoopCount());
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
 
             lastInstr = startInstruction;
@@ -1005,7 +1008,7 @@
 
     private AbstractBeginNode handleException(ValueNode exceptionObject, int bci) {
         assert bci == BytecodeFrame.BEFORE_BCI || bci == bci() : "invalid bci";
-        Debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci)));
+        debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci)));
 
         FrameStateBuilder dispatchState = frameState.copy();
         dispatchState.clearStack();
@@ -1244,7 +1247,7 @@
 
         exception.setStateAfter(createFrameState(bci(), exception));
         exception.setNext(handleException(exception, bci()));
-        EXPLICIT_EXCEPTIONS.increment();
+        EXPLICIT_EXCEPTIONS.increment(debug);
         return nonNullReceiver;
     }
 
@@ -1452,6 +1455,10 @@
             args[0] = emitExplicitExceptions(args[0]);
         }
 
+        if (initialInvokeKind == InvokeKind.Special && !targetMethod.isConstructor()) {
+            emitCheckForInvokeSuperSpecial(args);
+        }
+
         InlineInfo inlineInfo = null;
         try {
             currentInvoke = new CurrentInvoke(args, invokeKind, returnType);
@@ -1535,6 +1542,30 @@
         return invoke;
     }
 
+    /**
+     * Checks that the class of the receiver of an {@link Bytecodes#INVOKESPECIAL} in a method
+     * declared in an interface (i.e., a default method) is assignable to the interface. If not,
+     * then deoptimize so that the interpreter can throw an {@link IllegalAccessError}.
+     *
+     * This is a check not performed by the verifier and so must be performed at runtime.
+     *
+     * @param args arguments to an {@link Bytecodes#INVOKESPECIAL} implementing a direct call to a
+     *            method in a super class
+     */
+    protected void emitCheckForInvokeSuperSpecial(ValueNode[] args) {
+        ResolvedJavaType callingClass = method.getDeclaringClass();
+        if (callingClass.getHostClass() != null) {
+            callingClass = callingClass.getHostClass();
+        }
+        if (callingClass.isInterface()) {
+            ValueNode receiver = args[0];
+            TypeReference checkedType = TypeReference.createTrusted(graph.getAssumptions(), callingClass);
+            LogicNode condition = genUnique(createInstanceOf(checkedType, receiver, null));
+            FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, ClassCastException, None, false));
+            args[0] = append(PiNode.create(receiver, StampFactory.object(checkedType, true), fixedGuard));
+        }
+    }
+
     protected JavaTypeProfile getProfileForInvoke(InvokeKind invokeKind) {
         if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) {
             return profilingInfo.getTypeProfile(bci());
@@ -2095,9 +2126,6 @@
         StackTraceElement where = code.asStackTraceElement(bci());
         String s = format("%s%s (%s:%d) %s", nSpaces(getDepth()), method.isConstructor() ? method.format("%h.%n") : method.getName(), where.getFileName(), where.getLineNumber(),
                         format(format, args));
-        if (s.equals("decrypt (CipherBlockChainingSubstitutions.java:117) inlining call to CipherBlockChainingSubstitutions.decrypt(Object, byte[], int, int, byte[], int)")) {
-            System.console();
-        }
         TTY.println(s);
     }
 
@@ -2421,7 +2449,7 @@
                         firstLoopExit = loopExit;
                     }
                     lastLoopExit = loopExit;
-                    Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
+                    debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
                     newState.clearNonLiveLocals(targetBlock, liveness, true);
                     newState.insertLoopProxies(loopExit, getEntryState(loop));
                     loopExit.setStateAfter(newState.create(bci, loopExit));
@@ -2487,7 +2515,7 @@
             setEntryState(block, currentEntryState);
             currentEntryState.clearNonLiveLocals(block, liveness, true);
 
-            Debug.log("createTarget %s: first visit, result: %s", block, targetNode);
+            debug.log("createTarget %s: first visit, result: %s", block, targetNode);
             return result;
         }
 
@@ -2508,7 +2536,7 @@
             FixedNode result = target.fixed;
             getEntryState(block).merge(loopBegin, target.state);
 
-            Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
+            debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
             return result;
         }
         assert currentBlock == null || currentBlock.getId() < block.getId() : "must not be backward branch";
@@ -2549,7 +2577,7 @@
         getEntryState(block).merge(mergeNode, target.state);
         mergeNode.addForwardEnd(newEnd);
 
-        Debug.log("createTarget %s: merging state, result: %s", block, result);
+        debug.log("createTarget %s: merging state, result: %s", block, result);
         return result;
     }
 
@@ -2580,10 +2608,10 @@
         // Ignore blocks that have no predecessors by the time their bytecodes are parsed
         FixedWithNextNode firstInstruction = getFirstInstruction(block);
         if (firstInstruction == null) {
-            Debug.log("Ignoring block %s", block);
+            debug.log("Ignoring block %s", block);
             return;
         }
-        try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, firstInstruction, block.isLoopHeader)) {
+        try (Indent indent = debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, firstInstruction, block.isLoopHeader)) {
 
             lastInstr = firstInstruction;
             frameState = getEntryState(block);
@@ -2729,7 +2757,7 @@
              */
             setEntryState(block, frameState.copy());
 
-            Debug.log("  created loop header %s", loopBegin);
+            debug.log("  created loop header %s", loopBegin);
         } else if (lastInstr instanceof MergeNode) {
             /*
              * All inputs of non-loop phi nodes are known by now. We can infer the stamp for the
@@ -2738,7 +2766,7 @@
             frameState.inferPhiStamps((AbstractMergeNode) lastInstr);
         }
         assert lastInstr.next() == null : "instructions already appended at block " + block;
-        Debug.log("  frameState: %s", frameState);
+        debug.log("  frameState: %s", frameState);
 
         lastInstr = finishInstruction(lastInstr, frameState);
 
@@ -2746,7 +2774,7 @@
 
         stream.setBCI(block.startBci);
         int bci = block.startBci;
-        BytecodesParsed.add(block.endBci - bci);
+        BytecodesParsed.add(debug, block.endBci - bci);
 
         /* Reset line number for new block */
         if (graphBuilderConfig.insertFullInfopoints()) {
@@ -2807,7 +2835,7 @@
     }
 
     private DebugCloseable openNodeContext() {
-        if ((graphBuilderConfig.trackNodeSourcePosition() || Debug.isDumpEnabledForMethod()) && !parsingIntrinsic()) {
+        if ((graphBuilderConfig.trackNodeSourcePosition() || debug.isDumpEnabledForMethod()) && !parsingIntrinsic()) {
             return graph.withNodeSourcePosition(createBytecodePosition());
         }
         return null;
@@ -2870,7 +2898,7 @@
     }
 
     private boolean traceState() {
-        if (Debug.isEnabled() && TraceBytecodeParserLevel.getValue(options) >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
+        if (debug.isLogEnabled() && TraceBytecodeParserLevel.getValue(options) >= TRACELEVEL_STATE) {
             frameState.traceState();
         }
         return true;
@@ -3920,7 +3948,7 @@
         if (prob != null) {
             assert prob.length == numberOfCases;
         } else {
-            Debug.log("Missing probability (switch) in %s at bci %d", method, bci);
+            debug.log("Missing probability (switch) in %s at bci %d", method, bci);
             prob = new double[numberOfCases];
             for (int i = 0; i < numberOfCases; i++) {
                 prob[i] = 1.0d / numberOfCases;
@@ -4006,7 +4034,7 @@
         double probability = profilingInfo.getBranchTakenProbability(bci());
         if (probability < 0) {
             assert probability == -1 : "invalid probability";
-            Debug.log("missing probability in %s at bci %d", code, bci());
+            debug.log("missing probability in %s at bci %d", code, bci());
             probability = 0.5;
         }
 
@@ -4279,7 +4307,7 @@
     }
 
     protected boolean traceInstruction(int bci, int opcode, boolean blockStart) {
-        if (Debug.isEnabled() && TraceBytecodeParserLevel.getValue(options) >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) {
+        if (debug.isLogEnabled() && TraceBytecodeParserLevel.getValue(options) >= TRACELEVEL_INSTRUCTIONS) {
             traceInstructionHelper(bci, opcode, blockStart);
         }
         return true;
@@ -4300,7 +4328,7 @@
         if (!currentBlock.getJsrScope().isEmpty()) {
             sb.append(' ').append(currentBlock.getJsrScope());
         }
-        Debug.log("%s", sb);
+        debug.log("%s", sb);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -46,7 +46,7 @@
 import org.graalvm.compiler.core.common.PermanentBailoutException;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.java.BciBlockMapping.BciBlock;
 import org.graalvm.compiler.nodeinfo.Verbosity;
@@ -503,48 +503,50 @@
     }
 
     public void insertLoopProxies(LoopExitNode loopExit, FrameStateBuilder loopEntryState) {
+        DebugContext debug = graph.getDebug();
         for (int i = 0; i < localsSize(); i++) {
             ValueNode value = locals[i];
             if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 locals[i] = ProxyNode.forValue(value, loopExit, graph);
             }
         }
         for (int i = 0; i < stackSize(); i++) {
             ValueNode value = stack[i];
             if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 stack[i] = ProxyNode.forValue(value, loopExit, graph);
             }
         }
         for (int i = 0; i < lockedObjects.length; i++) {
             ValueNode value = lockedObjects[i];
             if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 lockedObjects[i] = ProxyNode.forValue(value, loopExit, graph);
             }
         }
     }
 
     public void insertProxies(Function<ValueNode, ValueNode> proxyFunction) {
+        DebugContext debug = graph.getDebug();
         for (int i = 0; i < localsSize(); i++) {
             ValueNode value = locals[i];
             if (value != null && value != TWO_SLOT_MARKER) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 locals[i] = proxyFunction.apply(value);
             }
         }
         for (int i = 0; i < stackSize(); i++) {
             ValueNode value = stack[i];
             if (value != null && value != TWO_SLOT_MARKER) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 stack[i] = proxyFunction.apply(value);
             }
         }
         for (int i = 0; i < lockedObjects.length; i++) {
             ValueNode value = lockedObjects[i];
             if (value != null) {
-                Debug.log(" inserting proxy for %s", value);
+                debug.log(" inserting proxy for %s", value);
                 lockedObjects[i] = proxyFunction.apply(value);
             }
         }
@@ -996,14 +998,15 @@
     }
 
     public void traceState() {
-        Debug.log("|   state [nr locals = %d, stack depth = %d, method = %s]", localsSize(), stackSize(), getMethod());
+        DebugContext debug = graph.getDebug();
+        debug.log("|   state [nr locals = %d, stack depth = %d, method = %s]", localsSize(), stackSize(), getMethod());
         for (int i = 0; i < localsSize(); ++i) {
             ValueNode value = locals[i];
-            Debug.log("|   local[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getStackKind().getJavaName(), value);
+            debug.log("|   local[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getStackKind().getJavaName(), value);
         }
         for (int i = 0; i < stackSize(); ++i) {
             ValueNode value = stack[i];
-            Debug.log("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getStackKind().getJavaName(), value);
+            debug.log("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getStackKind().getJavaName(), value);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java	Fri Jul 07 09:40:47 2017 -0700
@@ -76,7 +76,7 @@
 import static org.graalvm.compiler.bytecode.Bytecodes.RET;
 
 import org.graalvm.compiler.bytecode.BytecodeStream;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.java.BciBlockMapping.BciBlock;
 
 /**
@@ -86,9 +86,9 @@
 public abstract class LocalLiveness {
     protected final BciBlock[] blocks;
 
-    public static LocalLiveness compute(BytecodeStream stream, BciBlock[] blocks, int maxLocals, int loopCount) {
+    public static LocalLiveness compute(DebugContext debug, BytecodeStream stream, BciBlock[] blocks, int maxLocals, int loopCount) {
         LocalLiveness liveness = maxLocals <= 64 ? new SmallLocalLiveness(blocks, maxLocals, loopCount) : new LargeLocalLiveness(blocks, maxLocals, loopCount);
-        liveness.computeLiveness(stream);
+        liveness.computeLiveness(debug, stream);
         return liveness;
     }
 
@@ -96,7 +96,7 @@
         this.blocks = blocks;
     }
 
-    void computeLiveness(BytecodeStream stream) {
+    void computeLiveness(DebugContext debug, BytecodeStream stream) {
         for (BciBlock block : blocks) {
             computeLocalLiveness(stream, block);
         }
@@ -104,18 +104,18 @@
         boolean changed;
         int iteration = 0;
         do {
-            assert traceIteration(iteration);
+            assert traceIteration(debug, iteration);
             changed = false;
             for (int i = blocks.length - 1; i >= 0; i--) {
                 BciBlock block = blocks[i];
                 int blockID = block.getId();
-                assert traceStart(block, blockID);
+                assert traceStart(debug, block, blockID);
 
                 boolean blockChanged = (iteration == 0);
                 if (block.getSuccessorCount() > 0) {
                     int oldCardinality = liveOutCardinality(blockID);
                     for (BciBlock sux : block.getSuccessors()) {
-                        assert traceSuccessor(sux);
+                        assert traceSuccessor(debug, sux);
                         propagateLiveness(blockID, sux.getId());
                     }
                     blockChanged |= (oldCardinality != liveOutCardinality(blockID));
@@ -123,7 +123,7 @@
 
                 if (blockChanged) {
                     updateLiveness(blockID);
-                    assert traceEnd(block, blockID);
+                    assert traceEnd(debug, block, blockID);
                 }
                 changed |= blockChanged;
             }
@@ -131,29 +131,29 @@
         } while (changed);
     }
 
-    private static boolean traceIteration(int iteration) {
-        Debug.log("Iteration %d", iteration);
+    private static boolean traceIteration(DebugContext debug, int iteration) {
+        debug.log("Iteration %d", iteration);
         return true;
     }
 
-    private boolean traceEnd(BciBlock block, int blockID) {
-        if (Debug.isLogEnabled()) {
-            Debug.logv("  end   B%d  [%d, %d]  in: %s  out: %s  gen: %s  kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
+    private boolean traceEnd(DebugContext debug, BciBlock block, int blockID) {
+        if (debug.isLogEnabled()) {
+            debug.logv("  end   B%d  [%d, %d]  in: %s  out: %s  gen: %s  kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
                             debugLiveKill(blockID));
         }
         return true;
     }
 
-    private boolean traceSuccessor(BciBlock sux) {
-        if (Debug.isLogEnabled()) {
-            Debug.log("    Successor B%d: %s", sux.getId(), debugLiveIn(sux.getId()));
+    private boolean traceSuccessor(DebugContext debug, BciBlock sux) {
+        if (debug.isLogEnabled()) {
+            debug.log("    Successor B%d: %s", sux.getId(), debugLiveIn(sux.getId()));
         }
         return true;
     }
 
-    private boolean traceStart(BciBlock block, int blockID) {
-        if (Debug.isLogEnabled()) {
-            Debug.logv("  start B%d  [%d, %d]  in: %s  out: %s  gen: %s  kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
+    private boolean traceStart(DebugContext debug, BciBlock block, int blockID) {
+        if (debug.isLogEnabled()) {
+            debug.logv("  start B%d  [%d, %d]  in: %s  out: %s  gen: %s  kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
                             debugLiveKill(blockID));
         }
         return true;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/JTTTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/JTTTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,16 +23,18 @@
 package org.graalvm.compiler.jtt;
 
 import static java.lang.reflect.Modifier.isStatic;
+
 import java.util.Collections;
 import java.util.Set;
 
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
 
 import jdk.vm.ci.code.InstalledCode;
@@ -64,9 +66,10 @@
     }
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        StructuredGraph graph = super.parseEager(m, allowAssumptions, compilationId, options);
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        StructuredGraph graph = super.parse(builder, graphBuilderSuite);
         if (argsToBind != null) {
+            ResolvedJavaMethod m = graph.method();
             Object receiver = isStatic(m.getModifiers()) ? null : this;
             Object[] args = argsWithReceiver(receiver, argsToBind);
             JavaType[] parameterTypes = m.toParameterTypes();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/threads/Object_wait03.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/threads/Object_wait03.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,7 @@
 package org.graalvm.compiler.jtt.threads;
 
 import org.graalvm.compiler.core.common.CancellationBailoutException;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.jtt.JTTTest;
 import org.graalvm.compiler.nodes.Cancellable;
 import org.junit.Rule;
@@ -125,7 +125,8 @@
         } catch (CancellationBailoutException e) {
             String message = String.format("Compilation cancelled after " + COMPILATION_TIMEOUT_MS + " ms");
             // For diagnosing expectedly long compilations (GR-3853)
-            Debug.forceDump(lastCompiledGraph, message);
+            DebugContext debug = getDebugContext();
+            debug.forceDump(lastCompiledGraph, message);
             throw new AssertionError(message, e);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64PrefetchOp.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64PrefetchOp.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,6 +25,7 @@
 
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.COMPOSITE;
 
+import org.graalvm.compiler.asm.aarch64.AArch64Assembler.PrefetchMode;
 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
 import org.graalvm.compiler.lir.LIRInstructionClass;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
@@ -32,18 +33,18 @@
 public final class AArch64PrefetchOp extends AArch64LIRInstruction {
     public static final LIRInstructionClass<AArch64PrefetchOp> TYPE = LIRInstructionClass.create(AArch64PrefetchOp.class);
 
-    @SuppressWarnings("unused") private final int instr;  // AllocatePrefetchInstr
+    private final PrefetchMode mode;  // AllocatePrefetchInstr
     @Alive({COMPOSITE}) protected AArch64AddressValue address;
 
-    public AArch64PrefetchOp(AArch64AddressValue address, int instr) {
+    public AArch64PrefetchOp(AArch64AddressValue address, PrefetchMode mode) {
         super(TYPE);
         this.address = address;
-        this.instr = instr;
+        this.mode = mode;
     }
 
     @Override
     public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
-        // TODO implement prefetch
-        masm.nop();
+        // instr gets ignored!
+        masm.prfm(address.toAddress(), mode);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/phases/StackMoveOptimizationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/phases/StackMoveOptimizationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,8 +29,8 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.RedundantMoveElimination;
@@ -62,14 +62,15 @@
         // @formatter:on
     }
 
-    private static final DebugCounter eliminatedBackup = Debug.counter("StackMoveOptimizer[EliminatedScratchBackupRestore]");
+    private static final CounterKey eliminatedBackup = DebugContext.counter("StackMoveOptimizer[EliminatedScratchBackupRestore]");
 
     @Override
     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PostAllocationOptimizationContext context) {
         LIR lir = lirGenRes.getLIR();
+        DebugContext debug = lir.getDebug();
         for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
-            new Closure().process(instructions);
+            new Closure().process(debug, instructions);
         }
     }
 
@@ -83,7 +84,7 @@
         private AllocatableValue slot;
         private boolean removed = false;
 
-        public void process(List<LIRInstruction> instructions) {
+        public void process(DebugContext debug, List<LIRInstruction> instructions) {
             for (int i = 0; i < instructions.size(); i++) {
                 LIRInstruction inst = instructions.get(i);
 
@@ -92,7 +93,7 @@
 
                     if (reg != null && !reg.equals(move.getScratchRegister())) {
                         // end of trace & start of new
-                        replaceStackMoves(instructions);
+                        replaceStackMoves(debug, instructions);
                     }
 
                     // lazy initialize
@@ -114,7 +115,7 @@
 
                 } else if (begin != NONE) {
                     // end of trace
-                    replaceStackMoves(instructions);
+                    replaceStackMoves(debug, instructions);
                 }
             }
             // remove instructions
@@ -124,7 +125,7 @@
 
         }
 
-        private void replaceStackMoves(List<LIRInstruction> instructions) {
+        private void replaceStackMoves(DebugContext debug, List<LIRInstruction> instructions) {
             int size = dst.size();
             if (size > 1) {
                 AMD64MultiStackMove multiMove = new AMD64MultiStackMove(dst.toArray(new AllocatableValue[size]), src.toArray(new AllocatableValue[size]), reg, slot);
@@ -134,7 +135,7 @@
                 Collections.fill(instructions.subList(begin + 1, begin + size), null);
                 // removed
                 removed = true;
-                eliminatedBackup.add(size - 1);
+                eliminatedBackup.add(debug, size - 1);
             }
             // reset
             dst.clear();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.jtt/src/org/graalvm/compiler/lir/jtt/SPARCBranchBailoutTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.jtt/src/org/graalvm/compiler/lir/jtt/SPARCBranchBailoutTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,13 +24,15 @@
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.core.common.PermanentBailoutException;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRInstructionClass;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
@@ -82,9 +84,11 @@
     public void testBailoutOnBranchOverflow() throws Throwable {
         Assume.assumeTrue(getBackend().getTarget().arch instanceof SPARC);
         ResolvedJavaMethod m = getResolvedJavaMethod("testBranch");
+        DebugContext debug = getDebugContext();
         try {
-            try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) {
-                compile(m, null);
+            try (Scope s = debug.disable()) {
+                StructuredGraph graph = parseEager(m, AllowAssumptions.YES, debug);
+                compile(m, graph);
             }
         } catch (GraalError e) {
             Assert.assertEquals(PermanentBailoutException.class, e.getCause().getClass());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/CompositeValue.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/CompositeValue.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,8 +28,6 @@
 import java.lang.annotation.Target;
 import java.util.EnumSet;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
 
@@ -50,11 +48,8 @@
         OperandFlag[] value() default OperandFlag.REG;
     }
 
-    private static final DebugCounter COMPOSITE_VALUE_COUNT = Debug.counter("CompositeValues");
-
     public CompositeValue(ValueKind<?> kind) {
         super(kind);
-        COMPOSITE_VALUE_COUNT.increment();
         assert CompositeValueClass.get(getClass()) != null;
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ControlFlowOptimizer.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ControlFlowOptimizer.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,8 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase;
 
@@ -56,7 +56,7 @@
             this.lir = lir;
         }
 
-        private static final DebugCounter BLOCKS_DELETED = Debug.counter("BlocksDeleted");
+        private static final CounterKey BLOCKS_DELETED = DebugContext.counter("BlocksDeleted");
 
         /**
          * Checks whether a block can be deleted. Only blocks with exactly one successor and an
@@ -105,7 +105,7 @@
                         alignBlock(other);
                     }
 
-                    BLOCKS_DELETED.increment();
+                    BLOCKS_DELETED.increment(lir.getDebug());
                     blocks[i] = null;
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIR.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIR.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,6 +29,7 @@
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.StandardOp.BlockEndOp;
 import org.graalvm.compiler.lir.StandardOp.LabelOp;
 import org.graalvm.compiler.lir.gen.LIRGenerator;
@@ -62,15 +63,18 @@
 
     private final OptionValues options;
 
+    private final DebugContext debug;
+
     /**
      * Creates a new LIR instance for the specified compilation.
      */
-    public LIR(AbstractControlFlowGraph<?> cfg, AbstractBlockBase<?>[] linearScanOrder, AbstractBlockBase<?>[] codeEmittingOrder, OptionValues options) {
+    public LIR(AbstractControlFlowGraph<?> cfg, AbstractBlockBase<?>[] linearScanOrder, AbstractBlockBase<?>[] codeEmittingOrder, OptionValues options, DebugContext debug) {
         this.cfg = cfg;
         this.codeEmittingOrder = codeEmittingOrder;
         this.linearScanOrder = linearScanOrder;
         this.lirInstructions = new BlockMap<>(cfg);
         this.options = options;
+        this.debug = debug;
     }
 
     public AbstractControlFlowGraph<?> getControlFlowGraph() {
@@ -81,6 +85,10 @@
         return options;
     }
 
+    public DebugContext getDebug() {
+        return debug;
+    }
+
     /**
      * Determines if any instruction in the LIR has debug info associated with it.
      */
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRInstruction.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRInstruction.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.lir;
 
+import static jdk.vm.ci.code.ValueUtil.isStackSlot;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.COMPOSITE;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT;
@@ -34,7 +35,6 @@
 import static org.graalvm.compiler.lir.LIRInstruction.OperandMode.DEF;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandMode.TEMP;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
-import static jdk.vm.ci.code.ValueUtil.isStackSlot;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -44,8 +44,6 @@
 import java.util.EnumMap;
 import java.util.EnumSet;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
 import org.graalvm.compiler.lir.StandardOp.MoveOp;
@@ -204,13 +202,10 @@
      */
     private NodeSourcePosition position;
 
-    private static final DebugCounter LIR_NODE_COUNT = Debug.counter("LIRNodes");
-
     /**
      * Constructs a new LIR instruction.
      */
     public LIRInstruction(LIRInstructionClass<? extends LIRInstruction> c) {
-        LIR_NODE_COUNT.increment();
         instructionClass = c;
         assert c.getClazz() == this.getClass();
         id = -1;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,8 +32,8 @@
 
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
@@ -42,8 +42,8 @@
 import org.graalvm.compiler.lir.framemap.FrameMap;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase;
+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.code.RegisterArray;
@@ -57,7 +57,7 @@
  */
 public final class RedundantMoveElimination extends PostAllocationOptimizationPhase {
 
-    private static final DebugCounter deletedMoves = Debug.counter("RedundantMovesEliminated");
+    private static final CounterKey deletedMoves = DebugContext.counter("RedundantMovesEliminated");
 
     @Override
     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PostAllocationOptimizationContext context) {
@@ -136,8 +136,8 @@
          */
         @SuppressWarnings("try")
         private void doOptimize(LIR lir) {
-
-            try (Indent indent = Debug.logAndIndent("eliminate redundant moves")) {
+            DebugContext debug = lir.getDebug();
+            try (Indent indent = debug.logAndIndent("eliminate redundant moves")) {
 
                 callerSaveRegs = frameMap.getRegisterConfig().getCallerSaveRegisters();
 
@@ -168,7 +168,7 @@
         private static final int COMPLEXITY_LIMIT = 30000;
 
         private void initBlockData(LIR lir) {
-
+            DebugContext debug = lir.getDebug();
             AbstractBlockBase<?>[] blocks = lir.linearScanOrder();
             numRegs = 0;
 
@@ -203,7 +203,7 @@
              * Now we know the number of locations to optimize, so we can allocate the block states.
              */
             int numLocations = numRegs + stackIndices.size();
-            Debug.log("num locations = %d (regs = %d, stack = %d)", numLocations, numRegs, stackIndices.size());
+            debug.log("num locations = %d (regs = %d, stack = %d)", numLocations, numRegs, stackIndices.size());
             for (AbstractBlockBase<?> block : blocks) {
                 BlockData data = new BlockData(numLocations);
                 blockData.put(block, data);
@@ -222,7 +222,8 @@
         @SuppressWarnings("try")
         private boolean solveDataFlow(LIR lir) {
 
-            try (Indent indent = Debug.logAndIndent("solve data flow")) {
+            DebugContext debug = lir.getDebug();
+            try (Indent indent = debug.logAndIndent("solve data flow")) {
 
                 AbstractBlockBase<?>[] blocks = lir.linearScanOrder();
 
@@ -236,7 +237,7 @@
                 boolean changed;
                 do {
                     changed = false;
-                    try (Indent indent2 = Debug.logAndIndent("new iteration")) {
+                    try (Indent indent2 = debug.logAndIndent("new iteration")) {
 
                         for (AbstractBlockBase<?> block : blocks) {
 
@@ -262,7 +263,7 @@
                                  * control flow in case of an exception. So we assume a save default
                                  * for exception handler blocks.
                                  */
-                                Debug.log("kill all values at entry of block %d", block.getId());
+                                debug.log("kill all values at entry of block %d", block.getId());
                                 clearValues(data.entryState, valueNum);
                             } else {
                                 /*
@@ -278,7 +279,7 @@
                             valueNum += data.entryState.length;
 
                             if (newState || firstRound) {
-                                try (Indent indent3 = Debug.logAndIndent("update block %d", block.getId())) {
+                                try (Indent indent3 = debug.logAndIndent("update block %d", block.getId())) {
 
                                     /*
                                      * Derive the exit state from the entry state by iterating
@@ -289,7 +290,7 @@
                                     ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
 
                                     for (LIRInstruction op : instructions) {
-                                        valueNum = updateState(iterState, op, valueNum);
+                                        valueNum = updateState(debug, iterState, op, valueNum);
                                     }
                                     changed = true;
                                 }
@@ -322,14 +323,15 @@
          */
         @SuppressWarnings("try")
         private void eliminateMoves(LIR lir) {
+            DebugContext debug = lir.getDebug();
 
-            try (Indent indent = Debug.logAndIndent("eliminate moves")) {
+            try (Indent indent = debug.logAndIndent("eliminate moves")) {
 
                 AbstractBlockBase<?>[] blocks = lir.linearScanOrder();
 
                 for (AbstractBlockBase<?> block : blocks) {
 
-                    try (Indent indent2 = Debug.logAndIndent("eliminate moves in block %d", block.getId())) {
+                    try (Indent indent2 = debug.logAndIndent("eliminate moves in block %d", block.getId())) {
 
                         ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
                         BlockData data = blockData.get(block);
@@ -351,16 +353,14 @@
                                 int destIdx = getStateIdx(moveOp.getResult());
                                 if (sourceIdx >= 0 && destIdx >= 0 && iterState[sourceIdx] == iterState[destIdx]) {
                                     assert iterState[sourceIdx] != INIT_VALUE;
-                                    Debug.log("delete move %s", op);
+                                    debug.log("delete move %s", op);
                                     instructions.set(idx, null);
                                     hasDead = true;
-                                    if (deletedMoves.isEnabled()) {
-                                        deletedMoves.increment();
-                                    }
+                                    deletedMoves.increment(debug);
                                 }
                             }
                             // It doesn't harm if updateState is also called for a deleted move
-                            valueNum = updateState(iterState, op, valueNum);
+                            valueNum = updateState(debug, iterState, op, valueNum);
                         }
                         if (hasDead) {
                             instructions.removeAll(Collections.singleton(null));
@@ -374,9 +374,9 @@
          * Updates the state for one instruction.
          */
         @SuppressWarnings("try")
-        private int updateState(final int[] state, LIRInstruction op, int initValueNum) {
+        private int updateState(DebugContext debug, final int[] state, LIRInstruction op, int initValueNum) {
 
-            try (Indent indent = Debug.logAndIndent("update state for op %s, initial value num = %d", op, initValueNum)) {
+            try (Indent indent = debug.logAndIndent("update state for op %s, initial value num = %d", op, initValueNum)) {
                 if (isEligibleMove(op)) {
                     /*
                      * Handle the special case of a move instruction
@@ -387,7 +387,7 @@
                     if (sourceIdx >= 0 && destIdx >= 0) {
                         assert isObjectValue(state[sourceIdx]) || LIRKind.isValue(moveOp.getInput()) : "move op moves object but input is not defined as object " + moveOp;
                         state[destIdx] = state[sourceIdx];
-                        Debug.log("move value %d from %d to %d", state[sourceIdx], sourceIdx, destIdx);
+                        debug.log("move value %d from %d to %d", state[sourceIdx], sourceIdx, destIdx);
                         return initValueNum;
                     }
                 }
@@ -395,7 +395,7 @@
                 int valueNum = initValueNum;
 
                 if (op.destroysCallerSavedRegisters()) {
-                    Debug.log("kill all caller save regs");
+                    debug.log("kill all caller save regs");
 
                     for (Register reg : callerSaveRegs) {
                         if (reg.number < numRegs) {
@@ -424,7 +424,7 @@
                              * Assign a unique number to the output or temp location.
                              */
                             state[stateIdx] = encodeValueNum(opValueNum++, !LIRKind.isValue(operand));
-                            Debug.log("set def %d for register %s(%d): %d", opValueNum, operand, stateIdx, state[stateIdx]);
+                            debug.log("set def %d for register %s(%d): %d", opValueNum, operand, stateIdx, state[stateIdx]);
                         }
                     }
                 }
@@ -447,7 +447,7 @@
                      * all values which are referenced in the state (or all values which are not),
                      * but for simplicity we kill all values.
                      */
-                    Debug.log("kill all object values");
+                    debug.log("kill all object values");
                     clearValuesOfKindObject(state, valueNum);
                     valueNum += state.length;
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/IntervalWalker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/IntervalWalker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBinding;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBindingLists;
@@ -233,7 +233,8 @@
             walkTo(State.Active, opId);
             walkTo(State.Inactive, opId);
 
-            try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) {
+            DebugContext debug = allocator.getDebug();
+            try (Indent indent = debug.logAndIndent("walk to op %d", opId)) {
                 currentInterval.state = State.Active;
                 if (activateCurrent(currentInterval)) {
                     activeLists.addToListSortedByCurrentFromPositions(currentBinding, currentInterval);
@@ -257,8 +258,9 @@
     private void intervalMoved(Interval interval, State from, State to) {
         // intervalMoved() is called whenever an interval moves from one interval list to another.
         // In the implementation of this method it is prohibited to move the interval to any list.
-        if (Debug.isLogEnabled()) {
-            Debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator));
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator));
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java	Fri Jul 07 09:40:47 2017 -0700
@@ -40,8 +40,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIR;
@@ -128,6 +127,7 @@
     private final MoveFactory moveFactory;
 
     private final BlockMap<BlockData> blockData;
+    protected final DebugContext debug;
 
     /**
      * List of blocks in linear-scan order. This is only correct as long as the CFG does not change.
@@ -191,6 +191,7 @@
                     boolean neverSpillConstants) {
         this.ir = res.getLIR();
         this.res = res;
+        this.debug = ir.getDebug();
         this.moveFactory = spillMoveFactory;
         this.frameMapBuilder = res.getFrameMapBuilder();
         this.sortedBlocks = sortedBlocks;
@@ -220,6 +221,10 @@
         return ir.getOptions();
     }
 
+    public DebugContext getDebug() {
+        return debug;
+    }
+
     public int getFirstLirInstructionId(AbstractBlockBase<?> block) {
         int result = ir.getLIRforBlock(block).get(0).id();
         assert result >= 0;
@@ -642,8 +647,8 @@
         Interval result = interval.getSplitChildAtOpId(opId, mode, this);
 
         if (result != null) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("Split child at pos %d of interval %s is %s", opId, interval, result);
+            if (debug.isLogEnabled()) {
+                debug.log("Split child at pos %d of interval %s is %s", opId, interval, result);
             }
             return result;
         }
@@ -679,11 +684,11 @@
         /*
          * This is the point to enable debug logging for the whole register allocation.
          */
-        try (Indent indent = Debug.logAndIndent("LinearScan allocate")) {
+        try (Indent indent = debug.logAndIndent("LinearScan allocate")) {
 
             createLifetimeAnalysisPhase().apply(target, lirGenRes, context);
 
-            try (Scope s = Debug.scope("AfterLifetimeAnalysis", (Object) intervals)) {
+            try (DebugContext.Scope s = debug.scope("AfterLifetimeAnalysis", (Object) intervals)) {
                 sortIntervalsBeforeAllocation();
 
                 createRegisterAllocationPhase().apply(target, lirGenRes, context);
@@ -706,7 +711,7 @@
                     verifyIntervals();
                 }
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
     }
@@ -740,23 +745,23 @@
 
     @SuppressWarnings("try")
     public void printIntervals(String label) {
-        if (Debug.isLogEnabled()) {
-            try (Indent indent = Debug.logAndIndent("intervals %s", label)) {
+        if (debug.isLogEnabled()) {
+            try (Indent indent = debug.logAndIndent("intervals %s", label)) {
                 for (Interval interval : intervals) {
                     if (interval != null) {
-                        Debug.log("%s", interval.logString(this));
+                        debug.log("%s", interval.logString(this));
                     }
                 }
 
-                try (Indent indent2 = Debug.logAndIndent("Basic Blocks")) {
+                try (Indent indent2 = debug.logAndIndent("Basic Blocks")) {
                     for (int i = 0; i < blockCount(); i++) {
                         AbstractBlockBase<?> block = blockAt(i);
-                        Debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop());
+                        debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop());
                     }
                 }
             }
         }
-        Debug.dump(Debug.VERBOSE_LEVEL, new LinearScanIntervalDumper(Arrays.copyOf(intervals, intervalsSize)), label);
+        debug.dump(DebugContext.VERBOSE_LEVEL, new LinearScanIntervalDumper(Arrays.copyOf(intervals, intervalsSize)), label);
     }
 
     boolean verify() {
@@ -765,7 +770,7 @@
 
         verifyRegisters();
 
-        Debug.log("no errors found");
+        debug.log("no errors found");
 
         return true;
     }
@@ -773,7 +778,7 @@
     @SuppressWarnings("try")
     private void verifyRegisters() {
         // Enable this logging to get output for the verification process.
-        try (Indent indent = Debug.logAndIndent("verifying register allocation")) {
+        try (Indent indent = debug.logAndIndent("verifying register allocation")) {
             RegisterVerifier verifier = new RegisterVerifier(this);
             verifier.verify(blockAt(0));
         }
@@ -781,7 +786,7 @@
 
     @SuppressWarnings("try")
     protected void verifyIntervals() {
-        try (Indent indent = Debug.logAndIndent("verifying intervals")) {
+        try (Indent indent = debug.logAndIndent("verifying intervals")) {
             int len = intervalsSize;
 
             for (int i = 0; i < len; i++) {
@@ -793,33 +798,33 @@
                 i1.checkSplitChildren();
 
                 if (i1.operandNumber != i) {
-                    Debug.log("Interval %d is on position %d in list", i1.operandNumber, i);
-                    Debug.log(i1.logString(this));
+                    debug.log("Interval %d is on position %d in list", i1.operandNumber, i);
+                    debug.log(i1.logString(this));
                     throw new GraalError("");
                 }
 
                 if (isVariable(i1.operand) && i1.kind().equals(LIRKind.Illegal)) {
-                    Debug.log("Interval %d has no type assigned", i1.operandNumber);
-                    Debug.log(i1.logString(this));
+                    debug.log("Interval %d has no type assigned", i1.operandNumber);
+                    debug.log(i1.logString(this));
                     throw new GraalError("");
                 }
 
                 if (i1.location() == null) {
-                    Debug.log("Interval %d has no register assigned", i1.operandNumber);
-                    Debug.log(i1.logString(this));
+                    debug.log("Interval %d has no register assigned", i1.operandNumber);
+                    debug.log(i1.logString(this));
                     throw new GraalError("");
                 }
 
                 if (i1.first().isEndMarker()) {
-                    Debug.log("Interval %d has no Range", i1.operandNumber);
-                    Debug.log(i1.logString(this));
+                    debug.log("Interval %d has no Range", i1.operandNumber);
+                    debug.log(i1.logString(this));
                     throw new GraalError("");
                 }
 
                 for (Range r = i1.first(); !r.isEndMarker(); r = r.next) {
                     if (r.from >= r.to) {
-                        Debug.log("Interval %d has zero length range", i1.operandNumber);
-                        Debug.log(i1.logString(this));
+                        debug.log("Interval %d has zero length range", i1.operandNumber);
+                        debug.log(i1.logString(this));
                         throw new GraalError("");
                     }
                 }
@@ -866,7 +871,7 @@
 
     @SuppressWarnings("try")
     void verifyNoOopsInFixedIntervals() {
-        try (Indent indent = Debug.logAndIndent("verifying that no oops are in fixed intervals *")) {
+        try (Indent indent = debug.logAndIndent("verifying that no oops are in fixed intervals *")) {
             CheckConsumer checkConsumer = new CheckConsumer();
 
             Interval fixedIntervals;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,7 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import static org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
 import org.graalvm.compiler.lir.phases.LIRPhase;
@@ -47,13 +46,16 @@
 
     @SuppressWarnings("try")
     public final void apply(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context, boolean dumpLIR) {
-        try (Scope s = Debug.scope(getName(), this)) {
+        DebugContext debug = lirGenRes.getLIR().getDebug();
+        try (DebugContext.Scope s = debug.scope(getName(), this)) {
             run(target, lirGenRes, context);
-            if (dumpLIR && Debug.isDumpEnabled(Debug.VERBOSE_LEVEL)) {
-                Debug.dump(Debug.VERBOSE_LEVEL, lirGenRes.getLIR(), "After %s", getName());
+            if (dumpLIR) {
+                if (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) {
+                    debug.dump(DebugContext.VERBOSE_LEVEL, lirGenRes.getLIR(), "After %s", getName());
+                }
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanAssignLocationsPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanAssignLocationsPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,7 +33,7 @@
 import java.util.EnumSet;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.ConstantValue;
 import org.graalvm.compiler.lir.InstructionValueProcedure;
@@ -230,9 +230,10 @@
 
     @SuppressWarnings("try")
     private void assignLocations() {
-        try (Indent indent = Debug.logAndIndent("assign locations")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("assign locations")) {
             for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
-                try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) {
+                try (Indent indent2 = debug.logAndIndent("assign locations in block B%d", block.getId())) {
                     assignLocations(allocator.getLIR().getLIRforBlock(block));
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,7 +31,7 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -90,7 +90,8 @@
     // called once before assignment of register numbers
     @SuppressWarnings("try")
     void eliminateSpillMoves(LIRGenerationResult res) {
-        try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("Eliminating unnecessary spill moves")) {
 
             /*
              * collect all intervals that must be stored after their definition. The list is sorted
@@ -99,12 +100,12 @@
             Interval interval;
             interval = allocator.createUnhandledLists(mustStoreAtDefinition, null).getLeft();
             if (DetailedAsserts.getValue(allocator.getOptions())) {
-                checkIntervals(interval);
+                checkIntervals(debug, interval);
             }
 
             LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
             for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
-                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
+                try (Indent indent1 = debug.logAndIndent("Handle %s", block)) {
                     ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
                     int numInst = instructions.size();
 
@@ -125,14 +126,14 @@
                                  * Move target is a stack slot that is always correct, so eliminate
                                  * instruction.
                                  */
-                                if (Debug.isLogEnabled()) {
+                                if (debug.isLogEnabled()) {
                                     if (ValueMoveOp.isValueMoveOp(op)) {
                                         ValueMoveOp vmove = ValueMoveOp.asValueMoveOp(op);
-                                        Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(),
+                                        debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(),
                                                         allocator.operandNumber(vmove.getResult()), vmove.getResult(), block);
                                     } else {
                                         LoadConstantOp load = LoadConstantOp.asLoadConstantOp(op);
-                                        Debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(), block);
+                                        debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(), block);
                                     }
                                 }
 
@@ -170,8 +171,8 @@
                                         insertionBuffer.append(j + 1, move);
                                         move.setComment(res, "LSRAEliminateSpillMove: store at definition");
 
-                                        if (Debug.isLogEnabled()) {
-                                            Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
+                                        if (debug.isLogEnabled()) {
+                                            debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
                                         }
                                     }
                                 }
@@ -206,7 +207,7 @@
         return false;
     }
 
-    private static void checkIntervals(Interval interval) {
+    private static void checkIntervals(DebugContext debug, Interval interval) {
         Interval prev = null;
         Interval temp = interval;
         while (!temp.isEndMarker()) {
@@ -220,8 +221,8 @@
             assert temp.spillDefinitionPos() >= temp.from() : "invalid order";
             assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized";
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
+            if (debug.isLogEnabled()) {
+                debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
             }
 
             prev = temp;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -41,8 +41,7 @@
 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.util.BitMap2D;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
@@ -73,18 +72,20 @@
 public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase {
 
     protected final LinearScan allocator;
+    protected final DebugContext debug;
 
     /**
      * @param linearScan
      */
     protected LinearScanLifetimeAnalysisPhase(LinearScan linearScan) {
         allocator = linearScan;
+        debug = allocator.getDebug();
     }
 
     @Override
     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) {
         numberInstructions();
-        Debug.dump(Debug.VERBOSE_LEVEL, lirGenRes.getLIR(), "Before register allocation");
+        debug.dump(DebugContext.VERBOSE_LEVEL, lirGenRes.getLIR(), "Before register allocation");
         computeLocalLiveSets();
         computeGlobalLiveSets();
         buildIntervals(DetailedAsserts.getValue(allocator.getOptions()));
@@ -160,7 +161,7 @@
 
         // iterate all blocks
         for (final AbstractBlockBase<?> block : allocator.sortedBlocks()) {
-            try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) {
+            try (Indent indent = debug.logAndIndent("compute local live sets for block %s", block)) {
 
                 final BitSet liveGen = new BitSet(liveSize);
                 final BitSet liveKill = new BitSet(liveSize);
@@ -173,8 +174,8 @@
                         int operandNum = allocator.operandNumber(operand);
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen for operand %d(%s)", operandNum, operand);
+                            if (debug.isLogEnabled()) {
+                                debug.log("liveGen for operand %d(%s)", operandNum, operand);
                             }
                         }
                         if (block.getLoop() != null) {
@@ -191,8 +192,8 @@
                         int operandNum = allocator.operandNumber(operand);
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
+                            if (debug.isLogEnabled()) {
+                                debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
                             }
                         }
                     }
@@ -201,8 +202,8 @@
                     if (isVariable(operand)) {
                         int varNum = allocator.operandNumber(operand);
                         liveKill.set(varNum);
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("liveKill for operand %d(%s)", varNum, operand);
+                        if (debug.isLogEnabled()) {
+                            debug.log("liveKill for operand %d(%s)", varNum, operand);
                         }
                         if (block.getLoop() != null) {
                             intervalInLoop.setBit(varNum, block.getLoop().getIndex());
@@ -223,7 +224,7 @@
                 for (int j = 0; j < numInst; j++) {
                     final LIRInstruction op = instructions.get(j);
 
-                    try (Indent indent2 = Debug.logAndIndent("handle op %d: %s", op.id(), op)) {
+                    try (Indent indent2 = debug.logAndIndent("handle op %d: %s", op.id(), op)) {
                         op.visitEachInput(useConsumer);
                         op.visitEachAlive(useConsumer);
                         /*
@@ -242,9 +243,9 @@
                 blockSets.liveIn = new BitSet(liveSize);
                 blockSets.liveOut = new BitSet(liveSize);
 
-                if (Debug.isLogEnabled()) {
-                    Debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
-                    Debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
+                if (debug.isLogEnabled()) {
+                    debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
+                    debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
                 }
 
             }
@@ -282,7 +283,7 @@
      */
     @SuppressWarnings("try")
     protected void computeGlobalLiveSets() {
-        try (Indent indent = Debug.logAndIndent("compute global live sets")) {
+        try (Indent indent = debug.logAndIndent("compute global live sets")) {
             int numBlocks = allocator.blockCount();
             boolean changeOccurred;
             boolean changeOccurredInBlock;
@@ -296,7 +297,7 @@
             do {
                 changeOccurred = false;
 
-                try (Indent indent2 = Debug.logAndIndent("new iteration %d", iterationCount)) {
+                try (Indent indent2 = debug.logAndIndent("new iteration %d", iterationCount)) {
 
                     // iterate all blocks in reverse order
                     for (int i = numBlocks - 1; i >= 0; i--) {
@@ -346,8 +347,8 @@
                             liveIn.andNot(blockSets.liveKill);
                             liveIn.or(blockSets.liveGen);
 
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
+                            if (debug.isLogEnabled()) {
+                                debug.log("block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
                             }
                         }
                     }
@@ -381,18 +382,18 @@
 
     @SuppressWarnings("try")
     protected void reportFailure(int numBlocks) {
-        try (Scope s = Debug.forceLog()) {
-            try (Indent indent = Debug.logAndIndent("report failure")) {
+        try (DebugContext.Scope s = debug.forceLog()) {
+            try (Indent indent = debug.logAndIndent("report failure")) {
 
                 BitSet startBlockLiveIn = allocator.getBlockData(allocator.getLIR().getControlFlowGraph().getStartBlock()).liveIn;
-                try (Indent indent2 = Debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) {
+                try (Indent indent2 = debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) {
                     for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
                         Interval interval = allocator.intervalFor(operandNum);
                         if (interval != null) {
                             Value operand = interval.operand;
-                            Debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(operand));
+                            debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(debug, operand));
                         } else {
-                            Debug.log("var %d; missing operand", operandNum);
+                            debug.log("var %d; missing operand", operandNum);
                         }
                     }
                 }
@@ -404,20 +405,20 @@
                     Object valueForOperandFromDebugContext = null;
                     if (interval != null) {
                         operand = interval.operand;
-                        valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(operand);
+                        valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(debug, operand);
                     }
-                    try (Indent indent2 = Debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) {
+                    try (Indent indent2 = debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) {
 
                         ArrayDeque<AbstractBlockBase<?>> definedIn = new ArrayDeque<>();
                         EconomicSet<AbstractBlockBase<?>> usedIn = EconomicSet.create(Equivalence.IDENTITY);
                         for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
                             if (allocator.getBlockData(block).liveGen.get(operandNum)) {
                                 usedIn.add(block);
-                                try (Indent indent3 = Debug.logAndIndent("used in block B%d", block.getId())) {
+                                try (Indent indent3 = debug.logAndIndent("used in block B%d", block.getId())) {
                                     for (LIRInstruction ins : allocator.getLIR().getLIRforBlock(block)) {
-                                        try (Indent indent4 = Debug.logAndIndent("%d: %s", ins.id(), ins)) {
+                                        try (Indent indent4 = debug.logAndIndent("%d: %s", ins.id(), ins)) {
                                             ins.forEachState((liveStateOperand, mode, flags) -> {
-                                                Debug.log("operand=%s", liveStateOperand);
+                                                debug.log("operand=%s", liveStateOperand);
                                                 return liveStateOperand;
                                             });
                                         }
@@ -426,9 +427,9 @@
                             }
                             if (allocator.getBlockData(block).liveKill.get(operandNum)) {
                                 definedIn.add(block);
-                                try (Indent indent3 = Debug.logAndIndent("defined in block B%d", block.getId())) {
+                                try (Indent indent3 = debug.logAndIndent("defined in block B%d", block.getId())) {
                                     for (LIRInstruction ins : allocator.getLIR().getLIRforBlock(block)) {
-                                        Debug.log("%d: %s", ins.id(), ins);
+                                        debug.log("%d: %s", ins.id(), ins);
                                     }
                                 }
                             }
@@ -451,16 +452,16 @@
                                 }
                             }
                         }
-                        try (Indent indent3 = Debug.logAndIndent("**** offending usages are in: ")) {
+                        try (Indent indent3 = debug.logAndIndent("**** offending usages are in: ")) {
                             for (AbstractBlockBase<?> block : usedIn) {
-                                Debug.log("B%d", block.getId());
+                                debug.log("B%d", block.getId());
                             }
                         }
                     }
                 }
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -493,8 +494,8 @@
         // Register use position at even instruction id.
         interval.addUsePos(to & ~1, registerPriority, detailedAsserts);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name());
+        if (debug.isLogEnabled()) {
+            debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name());
         }
     }
 
@@ -512,8 +513,8 @@
         interval.addUsePos(tempPos, registerPriority, detailedAsserts);
         interval.addMaterializationValue(null);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
+        if (debug.isLogEnabled()) {
+            debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
         }
     }
 
@@ -543,8 +544,8 @@
              */
             interval.addRange(defPos, defPos + 1);
             interval.addUsePos(defPos, registerPriority, detailedAsserts);
-            if (Debug.isLogEnabled()) {
-                Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
+            if (debug.isLogEnabled()) {
+                debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
             }
         }
 
@@ -555,8 +556,8 @@
         }
         interval.addMaterializationValue(getMaterializedValue(op, operand, interval));
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
+        if (debug.isLogEnabled()) {
+            debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
         }
     }
 
@@ -574,8 +575,8 @@
                     assert allocator.blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block";
                     assert isVariable(move.getResult()) : "result of move must be a variable";
 
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("found move from stack slot %s to %s", slot, move.getResult());
+                    if (debug.isLogEnabled()) {
+                        debug.log("found move from stack slot %s to %s", slot, move.getResult());
                     }
                 }
 
@@ -600,8 +601,8 @@
                     } else {
                         from.setLocationHint(to);
                     }
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber);
+                    if (debug.isLogEnabled()) {
+                        debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber);
                     }
 
                     return registerHint;
@@ -685,7 +686,7 @@
     @SuppressWarnings("try")
     protected void buildIntervals(boolean detailedAsserts) {
 
-        try (Indent indent = Debug.logAndIndent("build intervals")) {
+        try (Indent indent = debug.logAndIndent("build intervals")) {
             InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> {
                 if (LinearScan.isVariableOrRegister(operand)) {
                     addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getValueKind(), detailedAsserts);
@@ -735,7 +736,7 @@
             for (int i = allocator.blockCount() - 1; i >= 0; i--) {
 
                 AbstractBlockBase<?> block = allocator.blockAt(i);
-                try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) {
+                try (Indent indent2 = debug.logAndIndent("handle block %d", block.getId())) {
 
                     ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
                     final int blockFrom = allocator.getFirstLirInstructionId(block);
@@ -749,8 +750,8 @@
                     for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) {
                         assert live.get(operandNum) : "should not stop here otherwise";
                         AllocatableValue operand = allocator.intervalFor(operandNum).operand;
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("live in %d: %s", operandNum, operand);
+                        if (debug.isLogEnabled()) {
+                            debug.log("live in %d: %s", operandNum, operand);
                         }
 
                         addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, LIRKind.Illegal, detailedAsserts);
@@ -773,7 +774,7 @@
                         final LIRInstruction op = instructions.get(j);
                         final int opId = op.id();
 
-                        try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) {
+                        try (Indent indent3 = debug.logAndIndent("handle inst %d: %s", opId, op)) {
 
                             // add a temp range for each register if operation destroys
                             // caller-save registers
@@ -783,8 +784,8 @@
                                         addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal, detailedAsserts);
                                     }
                                 }
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("operation destroys all caller-save registers");
+                                if (debug.isLogEnabled()) {
+                                    debug.log("operation destroys all caller-save registers");
                                 }
                             }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,8 +29,8 @@
 import java.util.Iterator;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -44,13 +44,15 @@
 
 public final class LinearScanOptimizeSpillPositionPhase extends LinearScanAllocationPhase {
 
-    private static final DebugCounter betterSpillPos = Debug.counter("BetterSpillPosition");
-    private static final DebugCounter betterSpillPosWithLowerProbability = Debug.counter("BetterSpillPositionWithLowerProbability");
+    private static final CounterKey betterSpillPos = DebugContext.counter("BetterSpillPosition");
+    private static final CounterKey betterSpillPosWithLowerProbability = DebugContext.counter("BetterSpillPositionWithLowerProbability");
 
     private final LinearScan allocator;
+    private DebugContext debug;
 
     LinearScanOptimizeSpillPositionPhase(LinearScan allocator) {
         this.allocator = allocator;
+        this.debug = allocator.getDebug();
     }
 
     @Override
@@ -61,7 +63,7 @@
 
     @SuppressWarnings("try")
     private void optimizeSpillPosition(LIRGenerationResult res) {
-        try (Indent indent0 = Debug.logAndIndent("OptimizeSpillPositions")) {
+        try (Indent indent0 = debug.logAndIndent("OptimizeSpillPositions")) {
             LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[allocator.getLIR().linearScanOrder().length];
             for (Interval interval : allocator.intervals()) {
                 optimizeInterval(insertionBuffers, interval, res);
@@ -83,7 +85,7 @@
         AbstractBlockBase<?> defBlock = allocator.blockForId(interval.spillDefinitionPos());
         AbstractBlockBase<?> spillBlock = null;
         Interval firstSpillChild = null;
-        try (Indent indent = Debug.logAndIndent("interval %s (%s)", interval, defBlock)) {
+        try (Indent indent = debug.logAndIndent("interval %s (%s)", interval, defBlock)) {
             for (Interval splitChild : interval.getSplitChildren()) {
                 if (isStackSlotValue(splitChild.location())) {
                     if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) {
@@ -94,7 +96,7 @@
                     // iterate all blocks where the interval has use positions
                     for (AbstractBlockBase<?> splitBlock : blocksForInterval(splitChild)) {
                         if (dominates(defBlock, splitBlock)) {
-                            Debug.log("Split interval %s, block %s", splitChild, splitBlock);
+                            debug.log("Split interval %s, block %s", splitChild, splitBlock);
                             if (spillBlock == null) {
                                 spillBlock = splitBlock;
                             } else {
@@ -106,17 +108,17 @@
                 }
             }
             if (spillBlock == null) {
-                Debug.log("not spill interval found");
+                debug.log("not spill interval found");
                 // no spill interval
                 interval.setSpillState(SpillState.StoreAtDefinition);
                 return;
             }
-            Debug.log(Debug.VERBOSE_LEVEL, "Spill block candidate (initial): %s", spillBlock);
+            debug.log(DebugContext.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_LEVEL, "Spill block candidate (after loop optimizaton): %s", spillBlock);
+            debug.log(DebugContext.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
@@ -129,25 +131,25 @@
             assert firstSpillChild != null;
             if (!defBlock.equals(spillBlock) && spillBlock.equals(allocator.blockForId(firstSpillChild.from()))) {
                 AbstractBlockBase<?> dom = spillBlock.getDominator();
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom);
+                if (debug.isLogEnabled()) {
+                    debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom);
                 }
                 spillBlock = dom;
             }
             if (defBlock.equals(spillBlock)) {
-                Debug.log(Debug.VERBOSE_LEVEL, "Definition is the best choice: %s", defBlock);
+                debug.log(DebugContext.VERBOSE_LEVEL, "Definition is the best choice: %s", defBlock);
                 // definition is the best choice
                 interval.setSpillState(SpillState.StoreAtDefinition);
                 return;
             }
             assert dominates(defBlock, spillBlock);
-            betterSpillPos.increment();
-            if (Debug.isLogEnabled()) {
-                Debug.log("Better spill position found (Block %s)", spillBlock);
+            betterSpillPos.increment(debug);
+            if (debug.isLogEnabled()) {
+                debug.log("Better spill position found (Block %s)", spillBlock);
             }
 
             if (defBlock.probability() <= spillBlock.probability()) {
-                Debug.log(Debug.VERBOSE_LEVEL, "Definition has lower probability %s (%f) is lower than spill block %s (%f)", defBlock, defBlock.probability(), spillBlock,
+                debug.log(DebugContext.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);
@@ -166,14 +168,14 @@
             AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval);
             LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation);
             move.setComment(res, "LSRAOptimizeSpillPos: optimize spill pos");
-            Debug.log(Debug.VERBOSE_LEVEL, "Insert spill move %s", move);
+            debug.log(DebugContext.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.
              */
             insertionBuffer.append(1, move);
 
-            betterSpillPosWithLowerProbability.increment();
+            betterSpillPosWithLowerProbability.increment(debug);
             interval.setSpillDefinitionPos(spillOpId);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,6 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra;
 
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
@@ -47,7 +46,7 @@
 
     @SuppressWarnings("try")
     void allocateRegisters() {
-        try (Indent indent = Debug.logAndIndent("allocate registers")) {
+        try (Indent indent = allocator.getDebug().logAndIndent("allocate registers")) {
             Interval precoloredIntervals;
             Interval notPrecoloredIntervals;
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,7 +26,7 @@
 import java.util.BitSet;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.StandardOp;
@@ -81,9 +81,10 @@
     }
 
     void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, MoveResolver moveResolver) {
+        DebugContext debug = allocator.getDebug();
         if (fromBlock.getSuccessorCount() <= 1) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
+            if (debug.isLogEnabled()) {
+                debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
             }
 
             ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(fromBlock);
@@ -96,8 +97,8 @@
             }
 
         } else {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
+            if (debug.isLogEnabled()) {
+                debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
             }
 
             if (allocator.detailedAsserts) {
@@ -123,7 +124,7 @@
      */
     @SuppressWarnings("try")
     protected void resolveDataFlow() {
-        try (Indent indent = Debug.logAndIndent("resolve data flow")) {
+        try (Indent indent = allocator.getDebug().logAndIndent("resolve data flow")) {
 
             MoveResolver moveResolver = allocator.createMoveResolver();
             BitSet blockCompleted = new BitSet(allocator.blockCount());
@@ -151,8 +152,9 @@
 
                     // prevent optimization of two consecutive blocks
                     if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId());
+                        DebugContext debug = allocator.getDebug();
+                        if (debug.isLogEnabled()) {
+                            debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId());
                         }
 
                         blockCompleted.set(block.getLinearScanNumber());
@@ -186,8 +188,9 @@
                      * blocks).
                      */
                     if (!alreadyResolved.get(toBlock.getLinearScanNumber())) {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId());
+                        DebugContext debug = allocator.getDebug();
+                        if (debug.isLogEnabled()) {
+                            debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId());
                         }
 
                         alreadyResolved.set(toBlock.getLinearScanNumber());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanWalker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanWalker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,7 +36,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.AllocatableRegisters;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.util.Util;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -323,11 +323,12 @@
     }
 
     int findOptimalSplitPos(Interval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) {
+        DebugContext debug = allocator.getDebug();
         int optimalSplitPos = -1;
         if (minSplitPos == maxSplitPos) {
             // trivial case, no optimization of split position possible
-            if (Debug.isLogEnabled()) {
-                Debug.log("min-pos and max-pos are equal, no optimization possible");
+            if (debug.isLogEnabled()) {
+                debug.log("min-pos and max-pos are equal, no optimization possible");
             }
             optimalSplitPos = minSplitPos;
 
@@ -350,8 +351,8 @@
             assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order";
             if (minBlock == maxBlock) {
                 // split position cannot be moved to block boundary : so split as late as possible
-                if (Debug.isLogEnabled()) {
-                    Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
+                if (debug.isLogEnabled()) {
+                    debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
                 }
                 optimalSplitPos = maxSplitPos;
 
@@ -362,15 +363,15 @@
                     // as mustHaveRegister) with a hole before each definition. When the register is
                     // needed
                     // for the second definition : an earlier reloading is unnecessary.
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("interval has hole just before maxSplitPos, so splitting at maxSplitPos");
+                    if (debug.isLogEnabled()) {
+                        debug.log("interval has hole just before maxSplitPos, so splitting at maxSplitPos");
                     }
                     optimalSplitPos = maxSplitPos;
 
                 } else {
                     // seach optimal block boundary between minSplitPos and maxSplitPos
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
+                    if (debug.isLogEnabled()) {
+                        debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
                     }
 
                     if (doLoopOptimization) {
@@ -378,8 +379,8 @@
                         // max-position :
                         // then split before this loop
                         int loopEndPos = interval.nextUsageExact(RegisterPriority.LiveAtLoopEnd, allocator.getLastLirInstructionId(minBlock) + 2);
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("loop optimization: loop end found at pos %d", loopEndPos);
+                        if (debug.isLogEnabled()) {
+                            debug.log("loop optimization: loop end found at pos %d", loopEndPos);
                         }
 
                         assert loopEndPos > minSplitPos : "invalid order";
@@ -393,8 +394,8 @@
                             // of the interval (normally, only mustHaveRegister causes a reloading)
                             AbstractBlockBase<?> loopBlock = allocator.blockForId(loopEndPos);
 
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("interval is used in loop that ends in block B%d, so trying to move maxBlock back from B%d to B%d", loopBlock.getId(), maxBlock.getId(), loopBlock.getId());
+                            if (debug.isLogEnabled()) {
+                                debug.log("interval is used in loop that ends in block B%d, so trying to move maxBlock back from B%d to B%d", loopBlock.getId(), maxBlock.getId(), loopBlock.getId());
                             }
                             assert loopBlock != minBlock : "loopBlock and minBlock must be different because block boundary is needed between";
 
@@ -402,12 +403,12 @@
                             optimalSplitPos = findOptimalSplitPos(minBlock, loopBlock, maxSpillPos);
                             if (optimalSplitPos == maxSpillPos) {
                                 optimalSplitPos = -1;
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("loop optimization not necessary");
+                                if (debug.isLogEnabled()) {
+                                    debug.log("loop optimization not necessary");
                                 }
                             } else {
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("loop optimization successful");
+                                if (debug.isLogEnabled()) {
+                                    debug.log("loop optimization successful");
                                 }
                             }
                         }
@@ -420,8 +421,8 @@
                 }
             }
         }
-        if (Debug.isLogEnabled()) {
-            Debug.log("optimal split position: %d", optimalSplitPos);
+        if (debug.isLogEnabled()) {
+            debug.log("optimal split position: %d", optimalSplitPos);
         }
 
         return optimalSplitPos;
@@ -433,8 +434,8 @@
     // 2) the right part is sorted into to the unhandled-list
     @SuppressWarnings("try")
     void splitBeforeUsage(Interval interval, int minSplitPos, int maxSplitPos) {
-
-        try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
 
             assert interval.from() < minSplitPos : "cannot split at start of interval";
             assert currentPosition < minSplitPos : "cannot split before current position";
@@ -450,8 +451,8 @@
             if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) {
                 // the split position would be just before the end of the interval
                 // . no split at all necessary
-                if (Debug.isLogEnabled()) {
-                    Debug.log("no split necessary because optimal split position is at end of interval");
+                if (debug.isLogEnabled()) {
+                    debug.log("no split necessary because optimal split position is at end of interval");
                 }
                 return;
             }
@@ -465,8 +466,8 @@
                 optimalSplitPos = (optimalSplitPos - 1) | 1;
             }
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("splitting at position %d", optimalSplitPos);
+            if (debug.isLogEnabled()) {
+                debug.log("splitting at position %d", optimalSplitPos);
             }
 
             assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary";
@@ -479,9 +480,9 @@
             assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position";
             unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart);
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("left interval  %s: %s", moveNecessary ? "      " : "", interval.logString(allocator));
-                Debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator));
+            if (debug.isLogEnabled()) {
+                debug.log("left interval  %s: %s", moveNecessary ? "      " : "", interval.logString(allocator));
+                debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator));
             }
         }
     }
@@ -492,6 +493,7 @@
     // 2) the right part is always on the stack and therefore ignored in further processing
     @SuppressWarnings("try")
     void splitForSpilling(Interval interval) {
+        DebugContext debug = allocator.getDebug();
         // calculate allowed range of splitting position
         int maxSplitPos = currentPosition;
         int previousUsage = interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos);
@@ -505,7 +507,7 @@
         }
         int minSplitPos = Math.max(previousUsage + 1, interval.from());
 
-        try (Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
+        try (Indent indent = debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
 
             assert interval.state == State.Active : "why spill interval that is not active?";
             assert interval.from() <= minSplitPos : "cannot split before start of interval";
@@ -516,7 +518,7 @@
             if (minSplitPos == interval.from()) {
                 // the whole interval is never used, so spill it entirely to memory
 
-                try (Indent indent2 = Debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) {
+                try (Indent indent2 = debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) {
 
                     assert interval.firstUsage(RegisterPriority.MustHaveRegister) > currentPosition : String.format("interval %s must not have use position before currentPosition %d", interval,
                                     currentPosition);
@@ -535,8 +537,8 @@
                         if (isRegister(parent.location())) {
                             if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) {
                                 // parent is never used, so kick it out of its assigned register
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber);
+                                if (debug.isLogEnabled()) {
+                                    debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber);
                                 }
                                 allocator.assignSpillSlot(parent);
                                 handleSpillSlot(parent);
@@ -562,7 +564,7 @@
                     optimalSplitPos = (optimalSplitPos - 1) | 1;
                 }
 
-                try (Indent indent2 = Debug.logAndIndent("splitting at position %d", optimalSplitPos)) {
+                try (Indent indent2 = debug.logAndIndent("splitting at position %d", optimalSplitPos)) {
                     assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary";
                     assert !allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 0) : "split pos must be even on block boundary";
 
@@ -572,8 +574,8 @@
                     changeSpillState(spilledPart, optimalSplitPos);
 
                     if (!allocator.isBlockBegin(optimalSplitPos)) {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber);
+                        if (debug.isLogEnabled()) {
+                            debug.log("inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber);
                         }
                         insertMove(optimalSplitPos, interval, spilledPart);
                     }
@@ -582,9 +584,9 @@
                     assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild";
                     spilledPart.makeCurrentSplitChild();
 
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("left interval: %s", interval.logString(allocator));
-                        Debug.log("spilled interval   : %s", spilledPart.logString(allocator));
+                    if (debug.isLogEnabled()) {
+                        debug.log("left interval: %s", interval.logString(allocator));
+                        debug.log("spilled interval   : %s", spilledPart.logString(allocator));
                     }
                 }
             }
@@ -697,7 +699,8 @@
 
     @SuppressWarnings("try")
     boolean allocFreeRegister(Interval interval) {
-        try (Indent indent = Debug.logAndIndent("trying to find free register for %s", interval)) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("trying to find free register for %s", interval)) {
 
             initUseLists(true);
             freeExcludeActiveFixed();
@@ -711,12 +714,12 @@
             // (either as a fixed register or a normal allocated register in the past)
             // only intervals overlapping with cur are processed, non-overlapping invervals can be
             // ignored safely
-            if (Debug.isLogEnabled()) {
+            if (debug.isLogEnabled()) {
                 // Enable this logging to see all register states
-                try (Indent indent2 = Debug.logAndIndent("state of registers:")) {
+                try (Indent indent2 = debug.logAndIndent("state of registers:")) {
                     for (Register register : availableRegs) {
                         int i = register.number;
-                        Debug.log("reg %d: usePos: %d", register.number, usePos[i]);
+                        debug.log("reg %d: usePos: %d", register.number, usePos[i]);
                     }
                 }
             }
@@ -725,8 +728,8 @@
             Interval locationHint = interval.locationHint(true);
             if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) {
                 hint = asRegister(locationHint.location());
-                if (Debug.isLogEnabled()) {
-                    Debug.log("hint register %d from interval %s", hint.number, locationHint);
+                if (debug.isLogEnabled()) {
+                    debug.log("hint register %d from interval %s", hint.number, locationHint);
                 }
             }
             assert interval.location() == null : "register already assigned to interval";
@@ -768,8 +771,8 @@
 
             splitPos = usePos[reg.number];
             interval.assignLocation(reg.asValue(interval.kind()));
-            if (Debug.isLogEnabled()) {
-                Debug.log("selected register %d", reg.number);
+            if (debug.isLogEnabled()) {
+                debug.log("selected register %d", reg.number);
             }
 
             assert splitPos > 0 : "invalid splitPos";
@@ -795,7 +798,8 @@
     // Split an Interval and spill it to memory so that cur can be placed in a register
     @SuppressWarnings("try")
     void allocLockedRegister(Interval interval) {
-        try (Indent indent = Debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) {
 
             // the register must be free at least until this position
             int firstUsage = interval.firstUsage(RegisterPriority.MustHaveRegister);
@@ -821,7 +825,7 @@
                 spillBlockInactiveFixed(interval);
                 spillCollectActiveAny(registerPriority);
                 spillCollectInactiveAny(interval);
-                if (Debug.isLogEnabled()) {
+                if (debug.isLogEnabled()) {
                     printRegisterState();
                 }
 
@@ -841,8 +845,8 @@
 
                 int regUsePos = (reg == null ? 0 : usePos[reg.number]);
                 if (regUsePos <= firstShouldHaveUsage) {
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos);
+                    if (debug.isLogEnabled()) {
+                        debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos);
                     }
 
                     if (firstUsage <= interval.from() + 1) {
@@ -852,7 +856,7 @@
                              * to spill an active interval that has a usage but do not require a
                              * register.
                              */
-                            Debug.log("retry with register priority must have register");
+                            debug.log("retry with register priority must have register");
                             continue;
                         }
                         String description = generateOutOfRegErrorMsg(interval, firstUsage, availableRegs);
@@ -861,7 +865,7 @@
                          * errors
                          */
                         allocator.assignSpillSlot(interval);
-                        Debug.dump(Debug.INFO_LEVEL, allocator.getLIR(), description);
+                        debug.dump(DebugContext.INFO_LEVEL, allocator.getLIR(), description);
                         allocator.printIntervals(description);
                         throw new OutOfRegistersException("LinearScan: no register found", description);
                     }
@@ -876,8 +880,8 @@
 
             int splitPos = blockPos[reg.number];
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("decided to use register %d", reg.number);
+            if (debug.isLogEnabled()) {
+                debug.log("decided to use register %d", reg.number);
             }
             assert splitPos > 0 : "invalid splitPos";
             assert needSplit || splitPos > interval.from() : "splitting interval at from";
@@ -901,12 +905,13 @@
 
     @SuppressWarnings("try")
     void printRegisterState() {
-        try (Indent indent2 = Debug.logAndIndent("state of registers:")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent2 = debug.logAndIndent("state of registers:")) {
             for (Register reg : availableRegs) {
                 int i = reg.number;
-                try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, intervals: ", i, usePos[i], blockPos[i])) {
+                try (Indent indent3 = debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, intervals: ", i, usePos[i], blockPos[i])) {
                     for (int j = 0; j < spillIntervals[i].size(); j++) {
-                        Debug.log("%s ", spillIntervals[i].get(j));
+                        debug.log("%s ", spillIntervals[i].get(j));
                     }
                 }
             }
@@ -925,8 +930,9 @@
             if (isOdd(pos)) {
                 // the current instruction is a call that blocks all registers
                 if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) {
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("free register cannot be available because all registers blocked by following call");
+                    DebugContext debug = allocator.getDebug();
+                    if (debug.isLogEnabled()) {
+                        debug.log("free register cannot be available because all registers blocked by following call");
                     }
 
                     // safety check that there is really no register available
@@ -1019,16 +1025,16 @@
     @SuppressWarnings("try")
     protected boolean activateCurrent(Interval interval) {
         boolean result = true;
-
-        try (Indent indent = Debug.logAndIndent("activating interval %s,  splitParent: %d", interval, interval.splitParent().operandNumber)) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("activating interval %s,  splitParent: %d", interval, interval.splitParent().operandNumber)) {
 
             final Value operand = interval.operand;
             if (interval.location() != null && isStackSlotValue(interval.location())) {
                 // activating an interval that has a stack slot assigned . split it at first use
                 // position
                 // used for method parameters
-                if (Debug.isLogEnabled()) {
-                    Debug.log("interval has spill slot assigned (method parameter) . split it before first use");
+                if (debug.isLogEnabled()) {
+                    debug.log("interval has spill slot assigned (method parameter) . split it before first use");
                 }
                 splitStackInterval(interval);
                 result = false;
@@ -1037,8 +1043,8 @@
                 if (interval.location() == null) {
                     // interval has not assigned register . normal allocation
                     // (this is the normal case for most intervals)
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("normal allocation of register");
+                    if (debug.isLogEnabled()) {
+                        debug.log("normal allocation of register");
                     }
 
                     // assign same spill slot to non-intersecting intervals
@@ -1063,8 +1069,8 @@
                 assert interval.isSplitChild();
                 assert interval.currentSplitChild() != null;
                 assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval";
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber);
+                if (debug.isLogEnabled()) {
+                    debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber);
                 }
 
                 insertMove(interval.from(), interval.currentSplitChild(), interval);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/MoveResolver.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/MoveResolver.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,8 +29,8 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.LIRKind;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
@@ -39,7 +39,6 @@
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.util.Equivalence;
 import org.graalvm.util.EconomicSet;
-
 import jdk.vm.ci.meta.AllocatableValue;
 import jdk.vm.ci.meta.Constant;
 import jdk.vm.ci.meta.Value;
@@ -48,7 +47,7 @@
  */
 public class MoveResolver {
 
-    private static final DebugCounter cycleBreakingSlotsAllocated = Debug.counter("LSRA[cycleBreakingSlotsAllocated]");
+    private static final CounterKey cycleBreakingSlotsAllocated = DebugContext.counter("LSRA[cycleBreakingSlotsAllocated]");
 
     private final LinearScan allocator;
 
@@ -207,7 +206,7 @@
             assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
             int direction = 1;
             setValueBlocked(location, direction);
-            Debug.log("block %s", location);
+            allocator.getDebug().log("block %s", location);
         }
     }
 
@@ -217,7 +216,7 @@
         if (mightBeBlocked(location)) {
             assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
             setValueBlocked(location, -1);
-            Debug.log("unblock %s", location);
+            allocator.getDebug().log("unblock %s", location);
         }
     }
 
@@ -276,8 +275,9 @@
         LIRInstruction move = createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location());
         insertionBuffer.append(insertIdx, move);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
         }
         return move;
     }
@@ -299,17 +299,19 @@
         LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr);
         insertionBuffer.append(insertIdx, move);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
         }
         return move;
     }
 
     @SuppressWarnings("try")
     private void resolveMappings() {
-        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("resolveMapping")) {
             assert verifyBeforeResolve();
-            if (Debug.isLogEnabled()) {
+            if (debug.isLogEnabled()) {
                 printMapping();
             }
 
@@ -389,7 +391,7 @@
         if (spillSlot == null) {
             spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind());
             fromInterval.setSpillSlot(spillSlot);
-            cycleBreakingSlotsAllocated.increment();
+            cycleBreakingSlotsAllocated.increment(allocator.getDebug());
         }
         spillInterval(spillCandidate, fromInterval, spillSlot);
     }
@@ -406,8 +408,9 @@
 
         spillInterval.assignLocation(spillSlot);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("created new Interval for spilling: %s", spillInterval);
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("created new Interval for spilling: %s", spillInterval);
         }
         blockRegisters(spillInterval);
 
@@ -420,7 +423,8 @@
 
     @SuppressWarnings("try")
     private void printMapping() {
-        try (Indent indent = Debug.logAndIndent("Mapping")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("Mapping")) {
             for (int i = mappingFrom.size() - 1; i >= 0; i--) {
                 Interval fromInterval = mappingFrom.get(i);
                 Interval toInterval = mappingTo.get(i);
@@ -431,7 +435,7 @@
                 } else {
                     from = fromInterval.location().toString();
                 }
-                Debug.log("move %s <- %s", from, to);
+                debug.log("move %s <- %s", from, to);
             }
         }
     }
@@ -460,10 +464,10 @@
     }
 
     public void addMapping(Interval fromInterval, Interval toInterval) {
-
+        DebugContext debug = allocator.getDebug();
         if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("no store to rematerializable interval %s needed", toInterval);
+            if (debug.isLogEnabled()) {
+                debug.log("no store to rematerializable interval %s needed", toInterval);
             }
             return;
         }
@@ -473,8 +477,8 @@
             addMapping(rematValue, toInterval);
             return;
         }
-        if (Debug.isLogEnabled()) {
-            Debug.log("add move mapping from %s to %s", fromInterval, toInterval);
+        if (debug.isLogEnabled()) {
+            debug.log("add move mapping from %s to %s", fromInterval, toInterval);
         }
 
         assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
@@ -486,8 +490,9 @@
     }
 
     public void addMapping(Constant fromOpr, Interval toInterval) {
-        if (Debug.isLogEnabled()) {
-            Debug.log("add move mapping from %s to %s", fromOpr, toInterval);
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            debug.log("add move mapping from %s to %s", fromOpr, toInterval);
         }
 
         mappingFrom.add(null);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/OptimizingLinearScanWalker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/OptimizingLinearScanWalker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,21 +22,20 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra;
 
-import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 import static jdk.vm.ci.code.ValueUtil.asRegister;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
+import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBinding;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBindingLists;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterPriority;
 import org.graalvm.compiler.lir.alloc.lsra.Interval.State;
 import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
-import org.graalvm.compiler.options.OptionKey;
 
 import jdk.vm.ci.code.Register;
 import jdk.vm.ci.meta.AllocatableValue;
@@ -65,23 +64,24 @@
             return;
         }
         assert isStackSlotValue(interval.location()) : "interval not assigned to a stack slot " + interval;
-        try (Scope s1 = Debug.scope("LSRAOptimization")) {
-            Debug.log("adding stack to unhandled list %s", interval);
+        DebugContext debug = allocator.getDebug();
+        try (DebugContext.Scope s1 = debug.scope("LSRAOptimization")) {
+            debug.log("adding stack to unhandled list %s", interval);
             unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Stack, interval);
         }
     }
 
     @SuppressWarnings("unused")
-    private static void printRegisterBindingList(RegisterBindingLists list, RegisterBinding binding) {
+    private static void printRegisterBindingList(DebugContext debug, RegisterBindingLists list, RegisterBinding binding) {
         for (Interval interval = list.get(binding); !interval.isEndMarker(); interval = interval.next) {
-            Debug.log("%s", interval);
+            debug.log("%s", interval);
         }
     }
 
     @SuppressWarnings("try")
     @Override
     void walk() {
-        try (Scope s = Debug.scope("OptimizingLinearScanWalker")) {
+        try (DebugContext.Scope s = allocator.getDebug().scope("OptimizingLinearScanWalker")) {
             for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
                 optimizeBlock(block);
             }
@@ -93,27 +93,28 @@
     private void optimizeBlock(AbstractBlockBase<?> block) {
         if (block.getPredecessorCount() == 1) {
             int nextBlock = allocator.getFirstLirInstructionId(block);
-            try (Scope s1 = Debug.scope("LSRAOptimization")) {
-                Debug.log("next block: %s (%d)", block, nextBlock);
+            DebugContext debug = allocator.getDebug();
+            try (DebugContext.Scope s1 = debug.scope("LSRAOptimization")) {
+                debug.log("next block: %s (%d)", block, nextBlock);
             }
-            try (Indent indent0 = Debug.indent()) {
+            try (Indent indent0 = debug.indent()) {
                 walkTo(nextBlock);
 
-                try (Scope s1 = Debug.scope("LSRAOptimization")) {
+                try (DebugContext.Scope s1 = debug.scope("LSRAOptimization")) {
                     boolean changed = true;
                     // we need to do this because the active lists might change
                     loop: while (changed) {
                         changed = false;
-                        try (Indent indent1 = Debug.logAndIndent("Active intervals: (block %s [%d])", block, nextBlock)) {
+                        try (Indent indent1 = debug.logAndIndent("Active intervals: (block %s [%d])", block, nextBlock)) {
                             for (Interval active = activeLists.get(RegisterBinding.Any); !active.isEndMarker(); active = active.next) {
-                                Debug.log("active   (any): %s", active);
+                                debug.log("active   (any): %s", active);
                                 if (optimize(nextBlock, block, active, RegisterBinding.Any)) {
                                     changed = true;
                                     break loop;
                                 }
                             }
                             for (Interval active = activeLists.get(RegisterBinding.Stack); !active.isEndMarker(); active = active.next) {
-                                Debug.log("active (stack): %s", active);
+                                debug.log("active (stack): %s", active);
                                 if (optimize(nextBlock, block, active, RegisterBinding.Stack)) {
                                     changed = true;
                                     break loop;
@@ -171,9 +172,10 @@
 
         assert isStackSlotValue(currentLocation) || isRegister(currentLocation) : "current location not a register or stack slot " + currentLocation;
 
-        try (Indent indent = Debug.logAndIndent("location differs: %s vs. %s", predecessorLocation, currentLocation)) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("location differs: %s vs. %s", predecessorLocation, currentLocation)) {
             // split current interval at current position
-            Debug.log("splitting at position %d", currentPos);
+            debug.log("splitting at position %d", currentPos);
 
             assert allocator.isBlockBegin(currentPos) && ((currentPos & 1) == 0) : "split pos must be even when on block boundary";
 
@@ -186,9 +188,9 @@
             assert splitPart.currentSplitChild() == currentInterval : "overwriting wrong currentSplitChild";
             splitPart.makeCurrentSplitChild();
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("left interval  : %s", currentInterval.logString(allocator));
-                Debug.log("right interval : %s", splitPart.logString(allocator));
+            if (debug.isLogEnabled()) {
+                debug.log("left interval  : %s", currentInterval.logString(allocator));
+                debug.log("right interval : %s", splitPart.logString(allocator));
             }
 
             if (Options.LSRAOptSplitOnly.getValue(allocator.getOptions())) {
@@ -199,7 +201,7 @@
                     splitRegisterInterval(splitPart, asRegister(predecessorLocation));
                 } else {
                     assert isStackSlotValue(predecessorLocation);
-                    Debug.log("assigning interval %s to %s", splitPart, predecessorLocation);
+                    debug.log("assigning interval %s to %s", splitPart, predecessorLocation);
                     splitPart.assignLocation(predecessorLocation);
                     // activate interval
                     activeLists.addToListSortedByCurrentFromPositions(RegisterBinding.Stack, splitPart);
@@ -224,13 +226,14 @@
         spillCollectActiveAny(RegisterPriority.LiveAtLoopEnd);
         spillCollectInactiveAny(interval);
 
-        if (Debug.isLogEnabled()) {
-            try (Indent indent2 = Debug.logAndIndent("state of registers:")) {
+        DebugContext debug = allocator.getDebug();
+        if (debug.isLogEnabled()) {
+            try (Indent indent2 = debug.logAndIndent("state of registers:")) {
                 for (Register register : availableRegs) {
                     int i = register.number;
-                    try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, intervals: ", i, usePos[i], blockPos[i])) {
+                    try (Indent indent3 = debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, intervals: ", i, usePos[i], blockPos[i])) {
                         for (int j = 0; j < spillIntervals[i].size(); j++) {
-                            Debug.log("%d ", spillIntervals[i].get(j).operandNumber);
+                            debug.log("%d ", spillIntervals[i].get(j).operandNumber);
                         }
                     }
                 }
@@ -245,7 +248,7 @@
         assert splitPos > 0 : "invalid splitPos";
         assert needSplit || splitPos > interval.from() : "splitting interval at from";
 
-        Debug.log("assigning interval %s to %s", interval, reg);
+        debug.log("assigning interval %s to %s", interval, reg);
         interval.assignLocation(reg.asValue(interval.kind()));
         if (needSplit) {
             // register not available for full interval : so split it
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/RegisterVerifier.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/RegisterVerifier.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,8 +30,7 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
@@ -84,7 +83,8 @@
 
     @SuppressWarnings("try")
     void verify(AbstractBlockBase<?> start) {
-        try (Scope s = Debug.scope("RegisterVerifier")) {
+        DebugContext debug = allocator.getDebug();
+        try (DebugContext.Scope s = debug.scope("RegisterVerifier")) {
             // setup input registers (method arguments) for first block
             Interval[] inputState = new Interval[stateSize()];
             setStateForBlock(start, inputState);
@@ -102,18 +102,19 @@
 
     @SuppressWarnings("try")
     private void processBlock(AbstractBlockBase<?> block) {
-        try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("processBlock B%d", block.getId())) {
             // must copy state because it is modified
             Interval[] inputState = copy(stateForBlock(block));
 
-            try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) {
+            try (Indent indent2 = debug.logAndIndent("Input-State of intervals:")) {
                 printState(inputState);
             }
 
             // process all operations of the block
             processOperations(block, inputState);
 
-            try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) {
+            try (Indent indent2 = debug.logAndIndent("Output-State of intervals:")) {
                 printState(inputState);
             }
 
@@ -125,18 +126,20 @@
     }
 
     protected void printState(Interval[] inputState) {
+        DebugContext debug = allocator.getDebug();
         for (int i = 0; i < stateSize(); i++) {
             Register reg = allocator.getRegisters().get(i);
             assert reg.number == i;
             if (inputState[i] != null) {
-                Debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
+                debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
             } else {
-                Debug.log(" %6s   __", reg);
+                debug.log(" %6s   __", reg);
             }
         }
     }
 
     private void processSuccessor(AbstractBlockBase<?> block, Interval[] inputState) {
+        DebugContext debug = allocator.getDebug();
         Interval[] savedState = stateForBlock(block);
 
         if (savedState != null) {
@@ -155,23 +158,23 @@
                         savedStateCorrect = false;
                         savedState[i] = null;
 
-                        Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
+                        debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
                     }
                 }
             }
 
             if (savedStateCorrect) {
                 // already processed block with correct inputState
-                Debug.log("processSuccessor B%d: previous visit already correct", block.getId());
+                debug.log("processSuccessor B%d: previous visit already correct", block.getId());
             } else {
                 // must re-visit this block
-                Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
+                debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
                 addToWorkList(block);
             }
 
         } else {
             // block was not processed before, so set initial inputState
-            Debug.log("processSuccessor B%d: initial visit", block.getId());
+            debug.log("processSuccessor B%d: initial visit", block.getId());
 
             setStateForBlock(block, copy(inputState));
             addToWorkList(block);
@@ -182,14 +185,14 @@
         return inputState.clone();
     }
 
-    static void statePut(Interval[] inputState, Value location, Interval interval) {
+    static void statePut(DebugContext debug, Interval[] inputState, Value location, Interval interval) {
         if (location != null && isRegister(location)) {
             Register reg = asRegister(location);
             int regNum = reg.number;
             if (interval != null) {
-                Debug.log("%s = %s", reg, interval.operand);
+                debug.log("%s = %s", reg, interval.operand);
             } else if (inputState[regNum] != null) {
-                Debug.log("%s = null", reg);
+                debug.log("%s = null", reg);
             }
 
             inputState[regNum] = interval;
@@ -209,6 +212,7 @@
 
     void processOperations(AbstractBlockBase<?> block, final Interval[] inputState) {
         ArrayList<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block);
+        DebugContext debug = allocator.getDebug();
         InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
 
             @Override
@@ -232,7 +236,7 @@
                     interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
                 }
 
-                statePut(inputState, interval.location(), interval.splitParent());
+                statePut(debug, inputState, interval.location(), interval.splitParent());
             }
         };
 
@@ -240,8 +244,8 @@
         for (int i = 0; i < ops.size(); i++) {
             final LIRInstruction op = ops.get(i);
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("%s", op.toStringWithIdPrefix());
+            if (debug.isLogEnabled()) {
+                debug.log("%s", op.toStringWithIdPrefix());
             }
 
             // check if input operands are correct
@@ -249,7 +253,7 @@
             // invalidate all caller save registers at calls
             if (op.destroysCallerSavedRegisters()) {
                 for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) {
-                    statePut(inputState, r.asValue(), null);
+                    statePut(debug, inputState, r.asValue(), null);
                 }
             }
             op.visitEachAlive(useConsumer);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScan.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScan.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,8 +24,7 @@
 
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.alloc.lsra.LinearScan;
 import org.graalvm.compiler.lir.alloc.lsra.LinearScanEliminateSpillMovePhase;
 import org.graalvm.compiler.lir.alloc.lsra.LinearScanLifetimeAnalysisPhase;
@@ -73,7 +72,7 @@
          * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value
          * matches (ie. there is no resolution move) are falsely detected as errors.
          */
-        try (Scope s1 = Debug.scope("Remove Phi In")) {
+        try (DebugContext.Scope s1 = debug.scope("Remove Phi In")) {
             for (AbstractBlockBase<?> toBlock : sortedBlocks()) {
                 if (toBlock.getPredecessorCount() > 1) {
                     SSAUtil.removePhiIn(getLIR(), toBlock);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanEliminateSpillMovePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanEliminateSpillMovePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,11 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra.ssa;
 
+import static jdk.vm.ci.code.ValueUtil.isRegister;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
-import static jdk.vm.ci.code.ValueUtil.isRegister;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.StandardOp.LabelOp;
@@ -82,8 +82,9 @@
         if (!block.getSuccessors()[0].equals(intStartBlock)) {
             return false;
         }
-        try (Indent indet = Debug.indent()) {
-            Debug.log("Is a move (%s) to phi interval %s", move, toInterval);
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.indent()) {
+            debug.log("Is a move (%s) to phi interval %s", move, toInterval);
         }
         return true;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanLifetimeAnalysisPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanLifetimeAnalysisPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,7 +24,7 @@
 
 import java.util.EnumSet;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
@@ -58,14 +58,14 @@
                 if (LinearScan.isVariableOrRegister(registerHint)) {
                     Interval from = allocator.getOrCreateInterval((AllocatableValue) registerHint);
 
-                    setHint(op, to, from);
-                    setHint(op, from, to);
+                    setHint(debug, op, to, from);
+                    setHint(debug, op, from, to);
                 }
             });
         }
     }
 
-    public static void setHint(final LIRInstruction op, Interval target, Interval source) {
+    public static void setHint(DebugContext debug, final LIRInstruction op, Interval target, Interval source) {
         Interval currentHint = target.locationHint(false);
         if (currentHint == null || currentHint.from() > target.from()) {
             /*
@@ -73,8 +73,8 @@
              * interval.
              */
             target.setLocationHint(source);
-            if (Debug.isLogEnabled()) {
-                Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber);
+            if (debug.isLogEnabled()) {
+                debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanResolveDataFlowPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/ssa/SSALinearScanResolveDataFlowPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,8 +30,8 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.alloc.lsra.Interval;
 import org.graalvm.compiler.lir.alloc.lsra.LinearScan;
@@ -44,8 +44,8 @@
 
 class SSALinearScanResolveDataFlowPhase extends LinearScanResolveDataFlowPhase {
 
-    private static final DebugCounter numPhiResolutionMoves = Debug.counter("SSA LSRA[numPhiResolutionMoves]");
-    private static final DebugCounter numStackToStackMoves = Debug.counter("SSA LSRA[numStackToStackMoves]");
+    private static final CounterKey numPhiResolutionMoves = DebugContext.counter("SSA LSRA[numPhiResolutionMoves]");
+    private static final CounterKey numStackToStackMoves = DebugContext.counter("SSA LSRA[numStackToStackMoves]");
 
     SSALinearScanResolveDataFlowPhase(LinearScan allocator) {
         super(allocator);
@@ -72,17 +72,18 @@
                     assert !isRegister(phiOut) : "phiOut is a register: " + phiOut;
                     assert !isRegister(phiIn) : "phiIn is a register: " + phiIn;
                     Interval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF);
+                    DebugContext debug = allocator.getDebug();
                     if (isConstantValue(phiOut)) {
-                        numPhiResolutionMoves.increment();
+                        numPhiResolutionMoves.increment(debug);
                         moveResolver.addMapping(asConstant(phiOut), toInterval);
                     } else {
                         Interval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF);
                         if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) {
-                            numPhiResolutionMoves.increment();
+                            numPhiResolutionMoves.increment(debug);
                             if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) {
                                 moveResolver.addMapping(fromInterval, toInterval);
                             } else {
-                                numStackToStackMoves.increment();
+                                numStackToStackMoves.increment(debug);
                                 moveResolver.addMapping(fromInterval, toInterval);
                             }
                         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/DefaultTraceRegisterAllocationPolicy.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/DefaultTraceRegisterAllocationPolicy.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,6 +43,7 @@
 import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.common.JVMCIError;
 import jdk.vm.ci.meta.AllocatableValue;
+import jdk.vm.ci.meta.PlatformKind;
 
 /**
  * Manages the selection of allocation strategies.
@@ -52,15 +53,32 @@
     public enum TraceRAPolicies {
         Default,
         LinearScanOnly,
-        BottomUpOnly
+        BottomUpOnly,
+        AlmostTrivial,
+        NumVariables,
+        Ratio,
+        Loops,
+        MaxFreq,
+        FreqBudget,
+        LoopRatio,
+        LoopBudget,
+        LoopMaxFreq
     }
 
     public static class Options {
         // @formatter:off
         @Option(help = "Use special allocator for trivial blocks.", type = OptionType.Debug)
         public static final OptionKey<Boolean> TraceRAtrivialBlockAllocator = new OptionKey<>(true);
+        @Option(help = "Use BottomUp if there is only one block with at most this number of instructions", type = OptionType.Debug)
+        public static final OptionKey<Integer> TraceRAalmostTrivialSize = new OptionKey<>(2);
+        @Option(help = "Use BottomUp for traces with low number of variables at block boundaries", type = OptionType.Debug)
+        public static final OptionKey<Integer> TraceRAnumVariables = new OptionKey<>(null);
         @Option(help = "Use LSRA / BottomUp ratio", type = OptionType.Debug)
         public static final OptionKey<Double> TraceRAbottomUpRatio = new OptionKey<>(0.0);
+        @Option(help = "Probability Threshold", type = OptionType.Debug)
+        public static final OptionKey<Double> TraceRAprobalilityThreshold = new OptionKey<>(0.8);
+        @Option(help = "Sum Probability Budget Threshold", type = OptionType.Debug)
+        public static final OptionKey<Double> TraceRAsumBudget = new OptionKey<>(0.5);
         @Option(help = "TraceRA allocation policy to use.", type = OptionType.Debug)
         public static final EnumOptionKey<TraceRAPolicies> TraceRAPolicy = new EnumOptionKey<>(TraceRAPolicies.Default);
         // @formatter:on
@@ -117,6 +135,216 @@
         }
     }
 
+    public static final class BottomUpAlmostTrivialStrategy extends BottomUpStrategy {
+
+        private final int trivialTraceSize;
+
+        public BottomUpAlmostTrivialStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            trivialTraceSize = Options.TraceRAalmostTrivialSize.getValue(plan.getOptions());
+        }
+
+        @Override
+        public boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            if (trace.size() != 1) {
+                return false;
+            }
+            return getLIR().getLIRforBlock(trace.getBlocks()[0]).size() <= trivialTraceSize;
+        }
+
+    }
+
+    public static final class BottomUpNumVariablesStrategy extends BottomUpStrategy {
+
+        private final int numVarLimit;
+
+        public BottomUpNumVariablesStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            Integer value = Options.TraceRAnumVariables.getValue(plan.getOptions());
+            if (value != null) {
+                numVarLimit = value;
+            } else {
+                /* Default to the number of allocatable word registers. */
+                PlatformKind wordKind = getTarget().arch.getWordKind();
+                int numWordRegisters = getRegisterAllocationConfig().getAllocatableRegisters(wordKind).allocatableRegisters.length;
+                numVarLimit = numWordRegisters;
+            }
+        }
+
+        @Override
+        public boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            GlobalLivenessInfo livenessInfo = getGlobalLivenessInfo();
+            int maxNumVars = livenessInfo.getBlockIn(trace.getBlocks()[0]).length;
+            for (AbstractBlockBase<?> block : trace.getBlocks()) {
+                maxNumVars = Math.max(maxNumVars, livenessInfo.getBlockOut(block).length);
+            }
+            return maxNumVars <= numVarLimit;
+        }
+
+    }
+
+    public static final class BottomUpRatioStrategy extends BottomUpStrategy {
+
+        private final double ratio;
+
+        public BottomUpRatioStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            ratio = Options.TraceRAbottomUpRatio.getValue(plan.getOptions());
+        }
+
+        @Override
+        public boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            double numTraces = getTraceBuilderResult().getTraces().size();
+            double traceId = trace.getId();
+            assert ratio >= 0 && ratio <= 1.0 : "Ratio out of range: " + ratio;
+            return (traceId / numTraces) >= ratio;
+        }
+
+    }
+
+    public abstract static class BottomUpLoopStrategyBase extends BottomUpStrategy {
+
+        public BottomUpLoopStrategyBase(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+        }
+
+        @Override
+        public final boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            if (getLIR().getControlFlowGraph().getLoops().isEmpty()) {
+                return shouldApplyToNoLoop(trace);
+            }
+            for (AbstractBlockBase<?> block : trace.getBlocks()) {
+                if (block.getLoopDepth() > 0) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        protected abstract boolean shouldApplyToNoLoop(Trace trace);
+
+    }
+
+    public static final class BottomUpLoopStrategy extends BottomUpLoopStrategyBase {
+
+        public BottomUpLoopStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+        }
+
+        @Override
+        protected boolean shouldApplyToNoLoop(Trace trace) {
+            // no loops at all -> use LSRA
+            return false;
+        }
+
+    }
+
+    public static final class BottomUpDelegatingLoopStrategy extends BottomUpLoopStrategyBase {
+
+        private final BottomUpStrategy delegate;
+
+        public BottomUpDelegatingLoopStrategy(TraceRegisterAllocationPolicy plan, BottomUpStrategy delegate) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected boolean shouldApplyToNoLoop(Trace trace) {
+            return delegate.shouldApplyTo(trace);
+        }
+
+    }
+
+    public static final class BottomUpMaxFrequencyStrategy extends BottomUpStrategy {
+
+        private final double maxMethodProbability;
+        private final double probabilityThreshold;
+
+        public BottomUpMaxFrequencyStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            maxMethodProbability = maxProbability(getLIR().getControlFlowGraph().getBlocks());
+            probabilityThreshold = Options.TraceRAprobalilityThreshold.getValue(plan.getOptions());
+        }
+
+        private static double maxProbability(AbstractBlockBase<?>[] blocks) {
+            double max = 0;
+            for (AbstractBlockBase<?> block : blocks) {
+                double probability = block.probability();
+                if (probability > max) {
+                    max = probability;
+                }
+            }
+            return max;
+        }
+
+        @Override
+        public boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            return maxProbability(trace.getBlocks()) / maxMethodProbability <= probabilityThreshold;
+        }
+
+    }
+
+    public static final class BottomUpFrequencyBudgetStrategy extends BottomUpStrategy {
+
+        private final double[] cumulativeTraceProbability;
+        private final double budget;
+
+        public BottomUpFrequencyBudgetStrategy(TraceRegisterAllocationPolicy plan) {
+            // explicitly specify the enclosing instance for the superclass constructor call
+            super(plan);
+            ArrayList<Trace> traces = getTraceBuilderResult().getTraces();
+            this.cumulativeTraceProbability = new double[traces.size()];
+            double sumMethodProbability = init(traces, this.cumulativeTraceProbability);
+            this.budget = sumMethodProbability * Options.TraceRAsumBudget.getValue(plan.getOptions());
+        }
+
+        private static double init(ArrayList<Trace> traces, double[] sumTraces) {
+            double sumMethod = 0;
+            for (Trace trace : traces) {
+                double traceSum = 0;
+                for (AbstractBlockBase<?> block : trace.getBlocks()) {
+                    traceSum += block.probability();
+                }
+                sumMethod += traceSum;
+                // store cumulative sum for trace
+                sumTraces[trace.getId()] = sumMethod;
+            }
+            return sumMethod;
+        }
+
+        @Override
+        public boolean shouldApplyTo(Trace trace) {
+            if (!super.shouldApplyTo(trace)) {
+                return false;
+            }
+            double cumTraceProb = cumulativeTraceProbability[trace.getId()];
+            return cumTraceProb > budget;
+        }
+
+    }
+
     public static final class TraceLinearScanStrategy extends AllocationStrategy {
 
         public TraceLinearScanStrategy(TraceRegisterAllocationPolicy plan) {
@@ -135,6 +363,7 @@
                         GlobalLivenessInfo livenessInfo, ArrayList<AllocationStrategy> strategies) {
             return new TraceLinearScanPhase(target, lirGenRes, spillMoveFactory, registerAllocationConfig, resultTraces, neverSpillConstant, cachedStackSlots, livenessInfo);
         }
+
     }
 
     public static TraceRegisterAllocationPolicy allocationPolicy(TargetDescription target, LIRGenerationResult lirGenRes, MoveFactory spillMoveFactory,
@@ -148,16 +377,42 @@
         switch (Options.TraceRAPolicy.getValue(options)) {
             case Default:
             case LinearScanOnly:
-                plan.appendStrategy(new TraceLinearScanStrategy(plan));
                 break;
             case BottomUpOnly:
                 plan.appendStrategy(new BottomUpStrategy(plan));
-                // Fallback
-                plan.appendStrategy(new TraceLinearScanStrategy(plan));
+                break;
+            case AlmostTrivial:
+                plan.appendStrategy(new BottomUpAlmostTrivialStrategy(plan));
+                break;
+            case NumVariables:
+                plan.appendStrategy(new BottomUpNumVariablesStrategy(plan));
+                break;
+            case Ratio:
+                plan.appendStrategy(new BottomUpRatioStrategy(plan));
+                break;
+            case Loops:
+                plan.appendStrategy(new BottomUpLoopStrategy(plan));
+                break;
+            case MaxFreq:
+                plan.appendStrategy(new BottomUpMaxFrequencyStrategy(plan));
+                break;
+            case FreqBudget:
+                plan.appendStrategy(new BottomUpFrequencyBudgetStrategy(plan));
+                break;
+            case LoopRatio:
+                plan.appendStrategy(new BottomUpDelegatingLoopStrategy(plan, new BottomUpRatioStrategy(plan)));
+                break;
+            case LoopMaxFreq:
+                plan.appendStrategy(new BottomUpDelegatingLoopStrategy(plan, new BottomUpMaxFrequencyStrategy(plan)));
+                break;
+            case LoopBudget:
+                plan.appendStrategy(new BottomUpDelegatingLoopStrategy(plan, new BottomUpFrequencyBudgetStrategy(plan)));
                 break;
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
+        // Fallback
+        plan.appendStrategy(new TraceLinearScanStrategy(plan));
         return plan;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessAnalysisPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessAnalysisPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,7 +31,7 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
@@ -65,7 +65,7 @@
 
     private final class Analyser {
 
-        private static final int LOG_LEVEL = Debug.INFO_LEVEL;
+        private static final int LOG_LEVEL = DebugContext.INFO_LEVEL;
 
         /**
          * Bit map specifying which operands are live upon entry to this block. These are values
@@ -140,9 +140,10 @@
         @SuppressWarnings("try")
         private void computeLiveness() {
             // iterate all blocks
+            DebugContext debug = lir.getDebug();
             for (int i = blocks.length - 1; i >= 0; i--) {
                 final AbstractBlockBase<?> block = blocks[i];
-                try (Indent indent = Debug.logAndIndent(LOG_LEVEL, "compute local live sets for block %s", block)) {
+                try (Indent indent = debug.logAndIndent(LOG_LEVEL, "compute local live sets for block %s", block)) {
 
                     final BitSet liveIn = mergeLiveSets(block);
                     setLiveOut(block, (BitSet) liveIn.clone());
@@ -159,8 +160,8 @@
                             processDef(liveIn, op, operand);
                         }
                     };
-                    if (Debug.isLogEnabled()) {
-                        Debug.log(LOG_LEVEL, "liveOut B%d %s", block.getId(), getLiveOut(block));
+                    if (debug.isLogEnabled()) {
+                        debug.log(LOG_LEVEL, "liveOut B%d %s", block.getId(), getLiveOut(block));
                     }
 
                     // iterate all instructions of the block
@@ -168,7 +169,7 @@
                     for (int j = instructions.size() - 1; j >= 0; j--) {
                         final LIRInstruction op = instructions.get(j);
 
-                        try (Indent indent2 = Debug.logAndIndent(LOG_LEVEL, "handle op %d: %s", op.id(), op)) {
+                        try (Indent indent2 = debug.logAndIndent(LOG_LEVEL, "handle op %d: %s", op.id(), op)) {
                             op.visitEachOutput(defConsumer);
                             op.visitEachTemp(defConsumer);
                             op.visitEachState(useConsumer);
@@ -182,8 +183,8 @@
                         handleLoopHeader(block.getLoop(), liveIn);
                     }
 
-                    if (Debug.isLogEnabled()) {
-                        Debug.log(LOG_LEVEL, "liveIn  B%d %s", block.getId(), getLiveIn(block));
+                    if (debug.isLogEnabled()) {
+                        debug.log(LOG_LEVEL, "liveIn  B%d %s", block.getId(), getLiveIn(block));
                     }
 
                 }
@@ -218,8 +219,9 @@
             if (isVariable(operand)) {
                 int operandNum = operandNumber(operand);
                 liveGen.set(operandNum);
-                if (Debug.isLogEnabled()) {
-                    Debug.log(LOG_LEVEL, "liveGen for operand %d(%s)", operandNum, operand);
+                DebugContext debug = lir.getDebug();
+                if (debug.isLogEnabled()) {
+                    debug.log(LOG_LEVEL, "liveGen for operand %d(%s)", operandNum, operand);
                 }
             }
         }
@@ -232,8 +234,9 @@
                     operands[operandNum] = operand;
                 }
                 liveGen.clear(operandNum);
-                if (Debug.isLogEnabled()) {
-                    Debug.log(LOG_LEVEL, "liveKill for operand %d(%s)", operandNum, operand);
+                DebugContext debug = lir.getDebug();
+                if (debug.isLogEnabled()) {
+                    debug.log(LOG_LEVEL, "liveKill for operand %d(%s)", operandNum, operand);
                 }
             }
         }
@@ -256,7 +259,7 @@
         public void finish() {
             // iterate all blocks in reverse order
             for (AbstractBlockBase<?> block : (AbstractBlockBase<?>[]) lir.getControlFlowGraph().getBlocks()) {
-                try (Indent indent = Debug.logAndIndent(LOG_LEVEL, "Finish Block %s", block)) {
+                try (Indent indent = lir.getDebug().logAndIndent(LOG_LEVEL, "Finish Block %s", block)) {
                     buildIncoming(block);
                     buildOutgoing(block);
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/GlobalLivenessInfo.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,7 @@
 import java.util.EnumSet;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
@@ -156,12 +155,13 @@
      */
     @SuppressWarnings("try")
     public boolean verify(LIR lir) {
-        try (Scope s = Debug.scope("Verify GlobalLivenessInfo", this)) {
+        DebugContext debug = lir.getDebug();
+        try (DebugContext.Scope s = debug.scope("Verify GlobalLivenessInfo", this)) {
             for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
                 assert verifyBlock(block, lir);
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
         return true;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,12 +25,11 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.MemUseTrackerKey;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
 import org.graalvm.compiler.lir.phases.LIRPhase;
@@ -57,23 +56,23 @@
     /**
      * Records time spent within {@link #apply}.
      */
-    private final DebugTimer timer;
+    private final TimerKey timer;
 
     /**
      * Records memory usage within {@link #apply}.
      */
-    private final DebugMemUseTracker memUseTracker;
+    private final MemUseTrackerKey memUseTracker;
 
     /**
      * Records the number of traces allocated with this phase.
      */
-    private final DebugCounter allocatedTraces;
+    private final CounterKey allocatedTraces;
 
     public static final class AllocationStatistics {
-        private final DebugCounter allocatedTraces;
+        private final CounterKey allocatedTraces;
 
         public AllocationStatistics(Class<?> clazz) {
-            allocatedTraces = Debug.counter("TraceRA[%s]", clazz);
+            allocatedTraces = DebugContext.counter("TraceRA[%s]", clazz);
         }
     }
 
@@ -110,19 +109,24 @@
 
     @SuppressWarnings("try")
     public final void apply(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, C context, boolean dumpTrace) {
-        try (Scope s = Debug.scope(getName(), this)) {
-            try (DebugCloseable a = timer.start(); DebugCloseable c = memUseTracker.start()) {
-                if (dumpTrace && Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                    Debug.dump(Debug.DETAILED_LEVEL, trace, "Before %s (Trace%s: %s)", getName(), trace.getId(), trace);
+        DebugContext debug = lirGenRes.getLIR().getDebug();
+        try (DebugContext.Scope s = debug.scope(getName(), this)) {
+            try (DebugCloseable a = timer.start(debug); DebugCloseable c = memUseTracker.start(debug)) {
+                if (dumpTrace) {
+                    if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                        debug.dump(DebugContext.DETAILED_LEVEL, trace, "Before %s (Trace%s: %s)", getName(), trace.getId(), trace);
+                    }
                 }
                 run(target, lirGenRes, trace, context);
-                allocatedTraces.increment();
-                if (dumpTrace && Debug.isDumpEnabled(Debug.VERBOSE_LEVEL)) {
-                    Debug.dump(Debug.VERBOSE_LEVEL, trace, "After %s (Trace%s: %s)", getName(), trace.getId(), trace);
+                allocatedTraces.increment(debug);
+                if (dumpTrace) {
+                    if (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) {
+                        debug.dump(DebugContext.VERBOSE_LEVEL, trace, "After %s (Trace%s: %s)", getName(), trace.getId(), trace);
+                    }
                 }
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceBuilderPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceBuilderPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,16 +34,16 @@
 import org.graalvm.compiler.core.common.alloc.TraceStatisticsPrinter;
 import org.graalvm.compiler.core.common.alloc.UniDirectionalTraceBuilder;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.phases.AllocationPhase;
 import org.graalvm.compiler.options.EnumOptionKey;
 import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.options.OptionKey;
 
 import jdk.vm.ci.code.TargetDescription;
 
@@ -73,15 +73,16 @@
 
         final TraceBuilderResult traceBuilderResult = getTraceBuilderResult(lir, startBlock, linearScanOrder);
 
-        if (Debug.isLogEnabled(Debug.BASIC_LEVEL)) {
+        DebugContext debug = lir.getDebug();
+        if (debug.isLogEnabled(DebugContext.BASIC_LEVEL)) {
             ArrayList<Trace> traces = traceBuilderResult.getTraces();
             for (int i = 0; i < traces.size(); i++) {
                 Trace trace = traces.get(i);
-                Debug.log(Debug.BASIC_LEVEL, "Trace %5d: %s%s", i, trace, isTrivialTrace(lirGenRes.getLIR(), trace) ? " (trivial)" : "");
+                debug.log(DebugContext.BASIC_LEVEL, "Trace %5d: %s%s", i, trace, isTrivialTrace(lirGenRes.getLIR(), trace) ? " (trivial)" : "");
             }
         }
-        TraceStatisticsPrinter.printTraceStatistics(traceBuilderResult, lirGenRes.getCompilationUnitName());
-        Debug.dump(Debug.VERBOSE_LEVEL, traceBuilderResult, "TraceBuilderResult");
+        TraceStatisticsPrinter.printTraceStatistics(debug, traceBuilderResult, lirGenRes.getCompilationUnitName());
+        debug.dump(DebugContext.VERBOSE_LEVEL, traceBuilderResult, "TraceBuilderResult");
         context.contextAdd(traceBuilderResult);
     }
 
@@ -90,14 +91,15 @@
 
         OptionValues options = lir.getOptions();
         TraceBuilder selectedTraceBuilder = Options.TraceBuilding.getValue(options);
-        Debug.log(Debug.BASIC_LEVEL, "Building Traces using %s", selectedTraceBuilder);
+        DebugContext debug = lir.getDebug();
+        debug.log(DebugContext.BASIC_LEVEL, "Building Traces using %s", selectedTraceBuilder);
         switch (Options.TraceBuilding.getValue(options)) {
             case SingleBlock:
-                return SingleBlockTraceBuilder.computeTraces(startBlock, linearScanOrder, pred);
+                return SingleBlockTraceBuilder.computeTraces(debug, startBlock, linearScanOrder, pred);
             case BiDirectional:
-                return BiDirectionalTraceBuilder.computeTraces(startBlock, linearScanOrder, pred);
+                return BiDirectionalTraceBuilder.computeTraces(debug, startBlock, linearScanOrder, pred);
             case UniDirectional:
-                return UniDirectionalTraceBuilder.computeTraces(startBlock, linearScanOrder, pred);
+                return UniDirectionalTraceBuilder.computeTraces(debug, startBlock, linearScanOrder, pred);
         }
         throw GraalError.shouldNotReachHere("Unknown trace building algorithm: " + Options.TraceBuilding.getValue(options));
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceGlobalMoveResolutionPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceGlobalMoveResolutionPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -37,7 +37,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -67,7 +67,9 @@
     }
 
     public static void resolve(TargetDescription target, LIRGenerationResult lirGenRes, TraceAllocationContext context) {
-        Debug.dump(Debug.VERBOSE_LEVEL, lirGenRes.getLIR(), "Before TraceGlobalMoveResultion");
+        LIR lir = lirGenRes.getLIR();
+        DebugContext debug = lir.getDebug();
+        debug.dump(DebugContext.VERBOSE_LEVEL, lir, "Before TraceGlobalMoveResultion");
         MoveFactory spillMoveFactory = context.spillMoveFactory;
         resolveGlobalDataFlow(context.resultTraces, lirGenRes, spillMoveFactory, target.arch, context.livenessInfo, context.registerAllocationConfig);
     }
@@ -79,12 +81,13 @@
         /* Resolve trace global data-flow mismatch. */
         TraceGlobalMoveResolver moveResolver = new TraceGlobalMoveResolver(lirGenRes, spillMoveFactory, registerAllocationConfig, arch);
 
-        try (Indent indent = Debug.logAndIndent("Trace global move resolution")) {
+        DebugContext debug = lir.getDebug();
+        try (Indent indent = debug.logAndIndent("Trace global move resolution")) {
             for (Trace trace : resultTraces.getTraces()) {
                 for (AbstractBlockBase<?> fromBlock : trace.getBlocks()) {
                     for (AbstractBlockBase<?> toBlock : fromBlock.getSuccessors()) {
                         if (resultTraces.getTraceForBlock(fromBlock) != resultTraces.getTraceForBlock(toBlock)) {
-                            try (Indent indent0 = Debug.logAndIndent("Handle trace edge from %s (Trace%d) to %s (Trace%d)", fromBlock, resultTraces.getTraceForBlock(fromBlock).getId(), toBlock,
+                            try (Indent indent0 = debug.logAndIndent("Handle trace edge from %s (Trace%d) to %s (Trace%d)", fromBlock, resultTraces.getTraceForBlock(fromBlock).getId(), toBlock,
                                             resultTraces.getTraceForBlock(toBlock).getId())) {
 
                                 final ArrayList<LIRInstruction> instructions;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceGlobalMoveResolver.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceGlobalMoveResolver.java	Fri Jul 07 09:40:47 2017 -0700
@@ -40,10 +40,11 @@
 
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
+import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.VirtualStackSlot;
@@ -64,8 +65,8 @@
  */
 public final class TraceGlobalMoveResolver extends TraceGlobalMoveResolutionPhase.MoveResolver {
 
-    private static final DebugCounter cycleBreakingSlotsAllocated = Debug.counter("TraceRA[cycleBreakingSlotsAllocated(global)]");
-    private static final DebugCounter cycleBreakingSlotsReused = Debug.counter("TraceRA[cycleBreakingSlotsReused(global)]");
+    private static final CounterKey cycleBreakingSlotsAllocated = DebugContext.counter("TraceRA[cycleBreakingSlotsAllocated(global)]");
+    private static final CounterKey cycleBreakingSlotsReused = DebugContext.counter("TraceRA[cycleBreakingSlotsReused(global)]");
 
     private int insertIdx;
     private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted
@@ -82,6 +83,7 @@
     private final OptionValues options;
     private final RegisterAllocationConfig registerAllocationConfig;
     private final LIRGenerationResult res;
+    private final DebugContext debug;
 
     private void setValueBlocked(Value location, int direction) {
         assert direction == 1 || direction == -1 : "out of bounds";
@@ -157,8 +159,10 @@
 
         FrameMap frameMap = frameMapBuilderTool.getFrameMap();
         this.firstVirtualStackIndex = !frameMap.frameNeedsAllocating() ? 0 : frameMap.currentFrameSize() + 1;
-        this.options = res.getLIR().getOptions();
         this.res = res;
+        LIR lir = res.getLIR();
+        this.options = lir.getOptions();
+        this.debug = lir.getDebug();
     }
 
     private boolean checkEmpty() {
@@ -228,7 +232,7 @@
         if (mightBeBlocked(location)) {
             assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
             setValueBlocked(location, 1);
-            Debug.log("block %s", location);
+            debug.log("block %s", location);
         }
     }
 
@@ -237,7 +241,7 @@
         if (mightBeBlocked(location)) {
             assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
             setValueBlocked(location, -1);
-            Debug.log("unblock %s", location);
+            debug.log("unblock %s", location);
         }
     }
 
@@ -324,8 +328,8 @@
         LIRInstruction move = createMove(fromOperand, toOperand);
         insertionBuffer.append(insertIdx, move);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("insert move from %s to %s at %d", fromOperand, toOperand, insertIdx);
+        if (debug.isLogEnabled()) {
+            debug.log("insert move from %s to %s at %d", fromOperand, toOperand, insertIdx);
         }
         return move;
     }
@@ -343,9 +347,9 @@
 
     @SuppressWarnings("try")
     private void resolveMappings() {
-        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
+        try (Indent indent = debug.logAndIndent("resolveMapping")) {
             assert verifyBeforeResolve();
-            if (Debug.isLogEnabled()) {
+            if (debug.isLogEnabled()) {
                 printMapping();
             }
 
@@ -411,21 +415,21 @@
 
         // create a new spill interval and assign a stack slot to it
         Value from = mappingFrom.get(spillCandidate);
-        try (Indent indent = Debug.logAndIndent("BreakCycle: %s", from)) {
+        try (Indent indent = debug.logAndIndent("BreakCycle: %s", from)) {
             AllocatableValue spillSlot = null;
             if (TraceRegisterAllocationPhase.Options.TraceRAreuseStackSlotsForMoveResolutionCycleBreaking.getValue(options) && !isStackSlotValue(from)) {
                 // don't use the stack slot if from is already the stack slot
                 Value fromStack = mappingFromStack.get(spillCandidate);
                 if (fromStack != null) {
                     spillSlot = (AllocatableValue) fromStack;
-                    cycleBreakingSlotsReused.increment();
-                    Debug.log("reuse slot for spilling: %s", spillSlot);
+                    cycleBreakingSlotsReused.increment(debug);
+                    debug.log("reuse slot for spilling: %s", spillSlot);
                 }
             }
             if (spillSlot == null) {
                 spillSlot = frameMapBuilder.allocateSpillSlot(from.getValueKind());
-                cycleBreakingSlotsAllocated.increment();
-                Debug.log("created new slot for spilling: %s", spillSlot);
+                cycleBreakingSlotsAllocated.increment(debug);
+                debug.log("created new slot for spilling: %s", spillSlot);
                 // insert a move from register to stack and update the mapping
                 LIRInstruction move = insertMove(from, spillSlot);
                 move.setComment(res, "TraceGlobalMoveResolver: breakCycle");
@@ -438,9 +442,9 @@
 
     @SuppressWarnings("try")
     private void printMapping() {
-        try (Indent indent = Debug.logAndIndent("Mapping")) {
+        try (Indent indent = debug.logAndIndent("Mapping")) {
             for (int i = mappingFrom.size() - 1; i >= 0; i--) {
-                Debug.log("move %s <- %s (%s)", mappingTo.get(i), mappingFrom.get(i), mappingFromStack.get(i));
+                debug.log("move %s <- %s (%s)", mappingTo.get(i), mappingFrom.get(i), mappingFromStack.get(i));
             }
         }
     }
@@ -454,8 +458,8 @@
 
     @Override
     public void addMapping(Value from, AllocatableValue to, Value fromStack) {
-        if (Debug.isLogEnabled()) {
-            Debug.log("add move mapping from %s to %s", from, to);
+        if (debug.isLogEnabled()) {
+            debug.log("add move mapping from %s to %s", from, to);
         }
 
         assert !from.equals(to) : "from and to interval equal: " + from;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,9 +26,8 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext;
@@ -63,10 +62,10 @@
         // @formatter:on
     }
 
-    private static final DebugCounter tracesCounter = Debug.counter("TraceRA[traces]");
+    private static final CounterKey tracesCounter = DebugContext.counter("TraceRA[traces]");
 
-    public static final DebugCounter globalStackSlots = Debug.counter("TraceRA[GlobalStackSlots]");
-    public static final DebugCounter allocatedStackSlots = Debug.counter("TraceRA[AllocatedStackSlots]");
+    public static final CounterKey globalStackSlots = DebugContext.counter("TraceRA[GlobalStackSlots]");
+    public static final CounterKey allocatedStackSlots = DebugContext.counter("TraceRA[AllocatedStackSlots]");
 
     @Override
     @SuppressWarnings("try")
@@ -74,6 +73,7 @@
         MoveFactory spillMoveFactory = context.spillMoveFactory;
         RegisterAllocationConfig registerAllocationConfig = context.registerAllocationConfig;
         LIR lir = lirGenRes.getLIR();
+        DebugContext debug = lir.getDebug();
         TraceBuilderResult resultTraces = context.contextLookup(TraceBuilderResult.class);
         GlobalLivenessInfo livenessInfo = context.contextLookup(GlobalLivenessInfo.class);
         assert livenessInfo != null;
@@ -86,16 +86,16 @@
         final TraceRegisterAllocationPolicy plan = DefaultTraceRegisterAllocationPolicy.allocationPolicy(target, lirGenRes, spillMoveFactory, registerAllocationConfig, cachedStackSlots, resultTraces,
                         neverSpillConstant, livenessInfo, lir.getOptions());
 
-        try (Scope s0 = Debug.scope("AllocateTraces", resultTraces, livenessInfo)) {
+        try (DebugContext.Scope s0 = debug.scope("AllocateTraces", resultTraces, livenessInfo)) {
             for (Trace trace : resultTraces.getTraces()) {
-                tracesCounter.increment();
+                tracesCounter.increment(debug);
                 TraceAllocationPhase<TraceAllocationContext> allocator = plan.selectStrategy(trace);
-                try (Indent i = Debug.logAndIndent("Allocating Trace%d: %s (%s)", trace.getId(), trace, allocator); Scope s = Debug.scope("AllocateTrace", trace)) {
+                try (Indent i = debug.logAndIndent("Allocating Trace%d: %s (%s)", trace.getId(), trace, allocator); DebugContext.Scope s = debug.scope("AllocateTrace", trace)) {
                     allocator.apply(target, lirGenRes, trace, traceContext);
                 }
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
 
         TraceGlobalMoveResolutionPhase.resolve(target, lirGenRes, traceContext);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPolicy.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/TraceRegisterAllocationPolicy.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,6 +31,7 @@
 import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
+import org.graalvm.compiler.options.OptionValues;
 
 import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.common.JVMCIError;
@@ -63,8 +64,23 @@
             return resultTraces;
         }
 
+        protected final GlobalLivenessInfo getGlobalLivenessInfo() {
+            return livenessInfo;
+        }
+
+        protected final RegisterAllocationConfig getRegisterAllocationConfig() {
+            return registerAllocationConfig;
+        }
+
+        protected final TargetDescription getTarget() {
+            return target;
+        }
+
         /**
          * Returns {@code true} if the allocation strategy should be used for {@code trace}.
+         *
+         * This method must not alter any state of the strategy, nor rely on being called in a
+         * specific trace order.
          */
         public abstract boolean shouldApplyTo(Trace trace);
 
@@ -99,6 +115,10 @@
         this.strategies = new ArrayList<>(3);
     }
 
+    protected OptionValues getOptions() {
+        return lirGenRes.getLIR().getOptions();
+    }
+
     public void appendStrategy(AllocationStrategy strategy) {
         strategies.add(strategy);
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/bu/BottomUpAllocator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/bu/BottomUpAllocator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -42,8 +42,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueProcedure;
 import org.graalvm.compiler.lir.LIR;
@@ -102,6 +101,7 @@
     private final BitSet allocatedBlocks;
     private final TraceBuilderResult resultTraces;
     private final TraceGlobalMoveResolver moveResolver;
+    private final DebugContext debug;
 
     /**
      * Maps from {@link Variable#index} to a spill stack slot. If
@@ -119,6 +119,7 @@
     public BottomUpAllocator(TargetDescription target, LIRGenerationResult lirGenRes, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig,
                     AllocatableValue[] cachedStackSlots, TraceBuilderResult resultTraces, boolean neverSpillConstant, GlobalLivenessInfo livenessInfo) {
         this.target = target;
+        this.debug = lirGenRes.getLIR().getDebug();
         this.lirGenRes = lirGenRes;
         this.spillMoveFactory = spillMoveFactory;
         this.registerAllocationConfig = registerAllocationConfig;
@@ -160,13 +161,13 @@
         int variableIndex = var.index;
         AllocatableValue cachedStackSlot = stackSlots[variableIndex];
         if (cachedStackSlot != null) {
-            TraceRegisterAllocationPhase.globalStackSlots.increment();
+            TraceRegisterAllocationPhase.globalStackSlots.increment(debug);
             assert cachedStackSlot.getValueKind().equals(var.getValueKind()) : "CachedStackSlot: kind mismatch? " + var.getValueKind() + " vs. " + cachedStackSlot.getValueKind();
             return cachedStackSlot;
         }
         VirtualStackSlot slot = lirGenRes.getFrameMapBuilder().allocateSpillSlot(var.getValueKind());
         stackSlots[variableIndex] = slot;
-        TraceRegisterAllocationPhase.allocatedStackSlots.increment();
+        TraceRegisterAllocationPhase.allocatedStackSlots.increment(debug);
         return slot;
     }
 
@@ -205,8 +206,8 @@
     private void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock) {
         LIR lir = lirGenRes.getLIR();
         if (fromBlock.getSuccessorCount() <= 1) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
+            if (debug.isLogEnabled()) {
+                debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
             }
 
             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(fromBlock);
@@ -219,8 +220,8 @@
             }
 
         } else {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
+            if (debug.isLogEnabled()) {
+                debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
             }
 
             if (DetailedAsserts.getValue(getLIR().getOptions())) {
@@ -284,7 +285,7 @@
         }
 
         private void setLastRegisterUsage(Register reg, int pos) {
-            Debug.log("Register %s last used %d", reg, pos);
+            debug.log("Register %s last used %d", reg, pos);
             lastRegisterUsage[reg.number] = pos;
         }
 
@@ -293,7 +294,7 @@
         }
 
         private void setLastRegisterKill(Register reg, int pos) {
-            Debug.log("Register %s killed %d", reg, pos);
+            debug.log("Register %s killed %d", reg, pos);
             lastRegisterKill[reg.number] = pos;
         }
 
@@ -309,7 +310,7 @@
             LIRInstruction move = spillMoveFactory.createMove(dst, src);
             insertInstructionsBefore.add(move);
             move.setComment(lirGenRes, "BottomUp: spill move before");
-            Debug.log("insert before %s", move);
+            debug.log("insert before %s", move);
         }
 
         private void insertSpillMoveAfter(AllocatableValue dst, Value src) {
@@ -318,9 +319,9 @@
                 LIRInstruction move = spillMoveFactory.createMove(dst, src);
                 insertInstructionsAfter.add(move);
                 move.setComment(lirGenRes, "BottomUp: spill move after");
-                Debug.log("insert after %s", move);
+                debug.log("insert after %s", move);
             } else {
-                Debug.log("Block end op. No from %s to %s necessary.", src, dst);
+                debug.log("Block end op. No from %s to %s necessary.", src, dst);
                 requireResolution = true;
             }
         }
@@ -346,7 +347,7 @@
 
         @SuppressWarnings("try")
         private void allocateTrace(Trace trace) {
-            try (Scope s = Debug.scope("BottomUpAllocator", trace.getBlocks()); Indent indent = Debug.logAndIndent("%s (Trace%d)", trace, trace.getId())) {
+            try (DebugContext.Scope s = debug.scope("BottomUpAllocator", trace.getBlocks()); Indent indent = debug.logAndIndent("%s (Trace%d)", trace, trace.getId())) {
                 AbstractBlockBase<?>[] blocks = trace.getBlocks();
                 int lastBlockIdx = blocks.length - 1;
                 AbstractBlockBase<?> successorBlock = blocks[lastBlockIdx];
@@ -367,7 +368,7 @@
                 }
                 resolveLoopBackEdge(trace);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
 
@@ -410,7 +411,7 @@
                 // insert move from variable
                 move = spillMoveFactory.createMove(dest, asVariable(phiOut));
             }
-            Debug.log("Inserting load %s", move);
+            debug.log("Inserting load %s", move);
             move.setComment(lirGenRes, "BottomUp: phi resolution");
             phiResolutionMoves.add(move);
         }
@@ -498,7 +499,7 @@
             // might be set in insertSpillMoveAfter
             requireResolution = false;
 
-            try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
+            try (Indent indent = debug.logAndIndent("handle block %s", block)) {
                 currentInstructions = getLIR().getLIRforBlock(block);
                 for (currentInstructionIndex = currentInstructions.size() - 1; currentInstructionIndex >= 0; currentInstructionIndex--) {
                     LIRInstruction inst = currentInstructions.get(currentInstructionIndex);
@@ -515,8 +516,8 @@
         @SuppressWarnings("try")
         private void allocateInstruction(LIRInstruction op, AbstractBlockBase<?> block) {
             assert op != null && op.id() == currentOpId;
-            try (Indent indent = Debug.logAndIndent("handle inst: %d: %s", op.id(), op)) {
-                try (Indent indent1 = Debug.logAndIndent("output pos")) {
+            try (Indent indent = debug.logAndIndent("handle inst: %d: %s", op.id(), op)) {
+                try (Indent indent1 = debug.logAndIndent("output pos")) {
                     // spill caller saved registers
                     if (op.destroysCallerSavedRegisters()) {
                         spillCallerSavedRegisters();
@@ -540,7 +541,7 @@
                         processIncoming(block, op);
                     }
                 }
-                try (Indent indent1 = Debug.logAndIndent("input pos")) {
+                try (Indent indent1 = debug.logAndIndent("input pos")) {
 
                     currentOpId++;
 
@@ -592,8 +593,8 @@
                     setLastRegisterUsage(reg, currentOpId);
                 }
             }
-            if (Debug.isLogEnabled()) {
-                Debug.log("operation destroys all caller-save registers");
+            if (debug.isLogEnabled()) {
+                debug.log("operation destroys all caller-save registers");
             }
         }
 
@@ -670,7 +671,7 @@
             setRegisterUsage(freeRegister, var);
             RegisterValue registerValue = freeRegister.asValue(var.getValueKind());
             setCurrentLocation(var, registerValue);
-            Debug.log("AllocateRegister[%5s] %s for %s", mode, freeRegister, var);
+            debug.log("AllocateRegister[%5s] %s for %s", mode, freeRegister, var);
             return registerValue;
         }
 
@@ -693,13 +694,13 @@
                         if (mode == OperandMode.ALIVE && killedAtDef(reg)) {
                             AllocatableValue spillSlot = allocateSpillSlot(var);
                             insertSpillMoveBefore(spillSlot, currentLocation);
-                            Debug.log("AllocateStackOrReg[%5s] temporary use %s for %s since current location %s is destroyed at def", mode, spillSlot, var, currentLocation);
+                            debug.log("AllocateStackOrReg[%5s] temporary use %s for %s since current location %s is destroyed at def", mode, spillSlot, var, currentLocation);
                             return spillSlot;
                         }
                         // update register usage
                         setLastRegisterUsage(reg, currentOpId);
                     }
-                    Debug.log(3, "AllocateStackOrReg[%5s] %s already in %s", mode, var, currentLocation);
+                    debug.log(3, "AllocateStackOrReg[%5s] %s already in %s", mode, var, currentLocation);
                     return currentLocation;
                 }
                 // no location available
@@ -716,7 +717,7 @@
                 setRegisterUsage(freeRegister, var);
                 RegisterValue registerValue = freeRegister.asValue(var.getValueKind());
                 setCurrentLocation(var, registerValue);
-                Debug.log("AllocateStackOrReg[%5s] %s for %s", mode, freeRegister, var);
+                debug.log("AllocateStackOrReg[%5s] %s for %s", mode, freeRegister, var);
                 return registerValue;
             }
             return value;
@@ -739,10 +740,10 @@
                     return reg;
                 }
             }
-            if (Debug.isLogEnabled()) {
-                try (Indent i = Debug.logAndIndent("All Registers occupied:")) {
+            if (debug.isLogEnabled()) {
+                try (Indent i = debug.logAndIndent("All Registers occupied:")) {
                     for (Register reg : availableRegs) {
-                        Debug.log("%6s: last used %4d %s", reg, getLastRegisterUsage(reg), getCurrentValue(reg));
+                        debug.log("%6s: last used %4d %s", reg, getLastRegisterUsage(reg), getCurrentValue(reg));
                     }
                 }
             }
@@ -792,9 +793,9 @@
             if (val != null && isVariable(val)) {
                 Variable var = asVariable(val);
                 setCurrentLocation(var, null);
-                Debug.log("Free Registers %s (was %s)", reg, var);
+                debug.log("Free Registers %s (was %s)", reg, var);
             } else {
-                Debug.log("Free Registers %s", reg);
+                debug.log("Free Registers %s", reg);
             }
         }
 
@@ -838,7 +839,7 @@
         private void spillVariable(AllocatableValue val, Register reg) {
             if (val != null && isVariable(val)) {
                 Variable var = asVariable(val);
-                Debug.log("Spill Variable %s from %s", var, reg);
+                debug.log("Spill Variable %s from %s", var, reg);
                 // insert reload
                 AllocatableValue spillSlot = allocateSpillSlot(var);
                 setCurrentLocation(var, spillSlot);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,8 +32,7 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
@@ -46,8 +45,6 @@
 import jdk.vm.ci.code.Register;
 import jdk.vm.ci.meta.Value;
 
-/**
- */
 final class RegisterVerifier {
 
     TraceLinearScan allocator;
@@ -88,7 +85,8 @@
 
     @SuppressWarnings("try")
     void verify(AbstractBlockBase<?> start) {
-        try (Scope s = Debug.scope("RegisterVerifier")) {
+        DebugContext debug = allocator.getDebug();
+        try (DebugContext.Scope s = debug.scope("RegisterVerifier")) {
             // setup input registers (method arguments) for first block
             TraceInterval[] inputState = new TraceInterval[stateSize()];
             setStateForBlock(start, inputState);
@@ -106,18 +104,19 @@
 
     @SuppressWarnings("try")
     private void processBlock(AbstractBlockBase<?> block) {
-        try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("processBlock B%d", block.getId())) {
             // must copy state because it is modified
             TraceInterval[] inputState = copy(stateForBlock(block));
 
-            try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) {
+            try (Indent indent2 = debug.logAndIndent("Input-State of intervals:")) {
                 printState(inputState);
             }
 
             // process all operations of the block
             processOperations(block, inputState);
 
-            try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) {
+            try (Indent indent2 = debug.logAndIndent("Output-State of intervals:")) {
                 printState(inputState);
             }
 
@@ -129,13 +128,14 @@
     }
 
     protected void printState(TraceInterval[] inputState) {
+        DebugContext debug = allocator.getDebug();
         for (int i = 0; i < stateSize(); i++) {
             Register reg = allocator.getRegisters().get(i);
             assert reg.number == i;
             if (inputState[i] != null) {
-                Debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
+                debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
             } else {
-                Debug.log(" %6s   __", reg);
+                debug.log(" %6s   __", reg);
             }
         }
     }
@@ -143,6 +143,7 @@
     private void processSuccessor(AbstractBlockBase<?> block, TraceInterval[] inputState) {
         TraceInterval[] savedState = stateForBlock(block);
 
+        DebugContext debug = allocator.getDebug();
         if (savedState != null) {
             // this block was already processed before.
             // check if new inputState is consistent with savedState
@@ -159,23 +160,23 @@
                         savedStateCorrect = false;
                         savedState[i] = null;
 
-                        Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
+                        debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
                     }
                 }
             }
 
             if (savedStateCorrect) {
                 // already processed block with correct inputState
-                Debug.log("processSuccessor B%d: previous visit already correct", block.getId());
+                debug.log("processSuccessor B%d: previous visit already correct", block.getId());
             } else {
                 // must re-visit this block
-                Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
+                debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
                 addToWorkList(block);
             }
 
         } else {
             // block was not processed before, so set initial inputState
-            Debug.log("processSuccessor B%d: initial visit", block.getId());
+            debug.log("processSuccessor B%d: initial visit", block.getId());
 
             setStateForBlock(block, copy(inputState));
             addToWorkList(block);
@@ -186,14 +187,14 @@
         return inputState.clone();
     }
 
-    static void statePut(TraceInterval[] inputState, Value location, TraceInterval interval) {
+    static void statePut(DebugContext debug, TraceInterval[] inputState, Value location, TraceInterval interval) {
         if (location != null && isRegister(location)) {
             Register reg = asRegister(location);
             int regNum = reg.number;
             if (interval != null) {
-                Debug.log("%s = v%d", reg, interval.operandNumber);
+                debug.log("%s = v%d", reg, interval.operandNumber);
             } else if (inputState[regNum] != null) {
-                Debug.log("%s = null", reg);
+                debug.log("%s = null", reg);
             }
 
             inputState[regNum] = interval;
@@ -213,6 +214,7 @@
 
     void processOperations(AbstractBlockBase<?> block, final TraceInterval[] inputState) {
         ArrayList<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block);
+        DebugContext debug = allocator.getDebug();
         InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
 
             @Override
@@ -236,7 +238,7 @@
                     interval = interval.getSplitChildAtOpId(op.id(), mode);
                 }
 
-                statePut(inputState, interval.location(), interval.splitParent());
+                statePut(debug, inputState, interval.location(), interval.splitParent());
             }
         };
 
@@ -244,8 +246,8 @@
         for (int i = 0; i < ops.size(); i++) {
             final LIRInstruction op = ops.get(i);
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("%s", op.toStringWithIdPrefix());
+            if (debug.isLogEnabled()) {
+                debug.log("%s", op.toStringWithIdPrefix());
             }
 
             // check if input operands are correct
@@ -253,7 +255,7 @@
             // invalidate all caller save registers at calls
             if (op.destroysCallerSavedRegisters()) {
                 for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) {
-                    statePut(inputState, r.asValue(), null);
+                    statePut(debug, inputState, r.asValue(), null);
                 }
             }
             op.visitEachAlive(useConsumer);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
@@ -52,8 +52,11 @@
     final void apply(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig,
                     TraceBuilderResult traceBuilderResult, TraceLinearScan allocator, boolean dumpLIR) {
         run(target, lirGenRes, trace, spillMoveFactory, registerAllocationConfig, traceBuilderResult, allocator);
-        if (dumpLIR && Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-            Debug.dump(Debug.DETAILED_LEVEL, trace, "After %s (Trace%s)", getName(), trace.getId());
+        if (dumpLIR) {
+            DebugContext debug = lirGenRes.getLIR().getDebug();
+            if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                debug.dump(DebugContext.DETAILED_LEVEL, trace, "After %s (Trace%s)", getName(), trace.getId());
+            }
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -38,7 +38,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.ConstantValue;
@@ -165,7 +165,8 @@
 
         @SuppressWarnings("try")
         private void assignBlock(AbstractBlockBase<?> block) {
-            try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) {
+            DebugContext debug = allocator.getDebug();
+            try (Indent indent2 = debug.logAndIndent("assign locations in block B%d", block.getId())) {
                 ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
                 handleBlockBegin(block, instructions);
                 int numInst = instructions.size();
@@ -312,7 +313,7 @@
 
         @SuppressWarnings("try")
         private void assign() {
-            try (Indent indent = Debug.logAndIndent("assign locations")) {
+            try (Indent indent = allocator.getDebug().logAndIndent("assign locations")) {
                 for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
                     assignBlock(block);
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,7 +34,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -75,7 +75,8 @@
     // called once before assignment of register numbers
     @SuppressWarnings("try")
     private static void eliminateSpillMoves(TraceLinearScan allocator, boolean shouldEliminateSpillMoves, TraceBuilderResult traceBuilderResult, LIRGenerationResult res) {
-        try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves: Trace%d", traceBuilderResult.getTraceForBlock(allocator.blockAt(0)).getId())) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("Eliminating unnecessary spill moves: Trace%d", traceBuilderResult.getTraceForBlock(allocator.blockAt(0)).getId())) {
             allocator.sortIntervalsBySpillPos();
 
             /*
@@ -84,19 +85,19 @@
              */
             TraceInterval interval = allocator.createUnhandledListBySpillPos(spilledIntervals);
             if (DetailedAsserts.getValue(allocator.getOptions())) {
-                checkIntervals(interval);
+                checkIntervals(debug, interval);
             }
-            if (Debug.isLogEnabled()) {
-                try (Indent indent2 = Debug.logAndIndent("Sorted intervals")) {
+            if (debug.isLogEnabled()) {
+                try (Indent indent2 = debug.logAndIndent("Sorted intervals")) {
                     for (TraceInterval i = interval; i != null; i = i.next) {
-                        Debug.log("%5d: %s", i.spillDefinitionPos(), i);
+                        debug.log("%5d: %s", i.spillDefinitionPos(), i);
                     }
                 }
             }
 
             LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
             for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
-                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
+                try (Indent indent1 = debug.logAndIndent("Handle %s", block)) {
                     ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
                     int numInst = instructions.size();
 
@@ -105,7 +106,7 @@
                     for (int j = 0; j < numInst; j++) {
                         LIRInstruction op = instructions.get(j);
                         int opId = op.id();
-                        try (Indent indent2 = Debug.logAndIndent("%5d %s", opId, op)) {
+                        try (Indent indent2 = debug.logAndIndent("%5d %s", opId, op)) {
 
                             if (opId == -1) {
                                 MoveOp move = MoveOp.asMoveOp(op);
@@ -119,13 +120,13 @@
                                      * Move target is a stack slot that is always correct, so
                                      * eliminate instruction.
                                      */
-                                    if (Debug.isLogEnabled()) {
+                                    if (debug.isLogEnabled()) {
                                         if (ValueMoveOp.isValueMoveOp(op)) {
                                             ValueMoveOp vmove = ValueMoveOp.asValueMoveOp(op);
-                                            Debug.log("eliminating move from interval %s to %s in block %s", vmove.getInput(), vmove.getResult(), block);
+                                            debug.log("eliminating move from interval %s to %s in block %s", vmove.getInput(), vmove.getResult(), block);
                                         } else {
                                             LoadConstantOp load = LoadConstantOp.asLoadConstantOp(op);
-                                            Debug.log("eliminating constant load from %s to %s in block %s", load.getConstant(), load.getResult(),
+                                            debug.log("eliminating constant load from %s to %s in block %s", load.getConstant(), load.getResult(),
                                                             block);
                                         }
                                     }
@@ -145,7 +146,7 @@
                                 assert interval == TraceInterval.EndMarker || (interval.isSplitParent() && SpillState.IN_MEMORY.contains(interval.spillState())) : "invalid interval";
 
                                 while (interval != TraceInterval.EndMarker && interval.spillDefinitionPos() == opId) {
-                                    Debug.log("handle %s", interval);
+                                    debug.log("handle %s", interval);
                                     if (!interval.canMaterialize() && interval.spillState() != SpillState.StartInMemory) {
 
                                         AllocatableValue fromLocation = interval.getSplitChildAtOpId(opId, OperandMode.DEF).location();
@@ -168,8 +169,8 @@
                                             insertionBuffer.append(j + 1, move);
                                             move.setComment(res, "TraceLSRAEliminateSpillMove: spill def pos");
 
-                                            if (Debug.isLogEnabled()) {
-                                                Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
+                                            if (debug.isLogEnabled()) {
+                                                debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
                                             }
                                         }
                                     }
@@ -226,7 +227,7 @@
         return curInterval.isSplitParent();
     }
 
-    private static void checkIntervals(TraceInterval interval) {
+    private static void checkIntervals(DebugContext debug, TraceInterval interval) {
         TraceInterval prev = null;
         TraceInterval temp = interval;
         while (temp != TraceInterval.EndMarker) {
@@ -241,8 +242,8 @@
             // assert temp.spillDefinitionPos() <= temp.from() + 2 :
             // "only intervals defined once at their start-pos can be optimized";
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
+            if (debug.isLogEnabled()) {
+                debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
             }
 
             prev = temp;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,8 +43,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
@@ -86,11 +85,13 @@
 
     public static final class Analyser {
         private final TraceLinearScan allocator;
+        private final DebugContext debug;
         private final TraceBuilderResult traceBuilderResult;
         private int numInstructions;
 
         public Analyser(TraceLinearScan allocator, TraceBuilderResult traceBuilderResult) {
             this.allocator = allocator;
+            this.debug = allocator.getDebug();
             this.traceBuilderResult = traceBuilderResult;
         }
 
@@ -205,8 +206,8 @@
         private void addFixedUse(RegisterValue reg, int from, int to) {
             FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
             interval.addRange(from, to);
-            if (Debug.isLogEnabled()) {
-                Debug.log("add fixed use: %s, at %d", interval, to);
+            if (debug.isLogEnabled()) {
+                debug.log("add fixed use: %s, at %d", interval, to);
             }
         }
 
@@ -217,8 +218,8 @@
             // Register use position at even instruction id.
             interval.addUsePos(to & ~1, registerPriority, allocator.getOptions());
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name());
+            if (debug.isLogEnabled()) {
+                debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name());
             }
         }
 
@@ -249,12 +250,12 @@
                  * Dead value - make vacuous interval also add register priority for dead intervals
                  */
                 interval.addRange(defPos, defPos + 1);
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos);
+                if (debug.isLogEnabled()) {
+                    debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos);
                 }
             }
-            if (Debug.isLogEnabled()) {
-                Debug.log("add fixed def: %s, at %d", interval, defPos);
+            if (debug.isLogEnabled()) {
+                debug.log("add fixed def: %s, at %d", interval, defPos);
             }
         }
 
@@ -268,8 +269,8 @@
                  * Dead value - make vacuous interval also add register priority for dead intervals
                  */
                 interval.addRange(defPos, defPos + 1);
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
+                if (debug.isLogEnabled()) {
+                    debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
                 }
             } else {
                 /*
@@ -290,8 +291,8 @@
             }
             interval.addMaterializationValue(getMaterializedValue(op, operand, interval, allocator.neverSpillConstants(), allocator.getSpillMoveFactory()));
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
+            if (debug.isLogEnabled()) {
+                debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
             }
             return interval;
         }
@@ -311,8 +312,8 @@
         private void addFixedTemp(RegisterValue reg, int tempPos) {
             FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
             interval.addRange(tempPos, tempPos + 1);
-            if (Debug.isLogEnabled()) {
-                Debug.log("add fixed temp: %s, at %d", interval, tempPos);
+            if (debug.isLogEnabled()) {
+                debug.log("add fixed temp: %s, at %d", interval, tempPos);
             }
         }
 
@@ -328,8 +329,8 @@
             interval.addUsePos(tempPos, registerPriority, allocator.getOptions());
             interval.addMaterializationValue(null);
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
+            if (debug.isLogEnabled()) {
+                debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
             }
         }
 
@@ -395,7 +396,7 @@
                                 fromValue = (AllocatableValue) targetValue;
                                 toValue = (AllocatableValue) registerHint;
                             }
-                            Debug.log("addRegisterHint %s to %s", fromValue, toValue);
+                            debug.log("addRegisterHint %s to %s", fromValue, toValue);
                             final TraceInterval to;
                             final IntervalHint from;
                             if (isRegister(toValue)) {
@@ -411,8 +412,8 @@
                             }
 
                             to.setLocationHint(from);
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
+                            if (debug.isLogEnabled()) {
+                                debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
                             }
 
                             return registerHint;
@@ -469,7 +470,7 @@
         @SuppressWarnings("try")
         private void buildIntervals() {
 
-            try (Indent indent = Debug.logAndIndent("build intervals")) {
+            try (Indent indent = debug.logAndIndent("build intervals")) {
 
                 // create a list with all caller-save registers (cpu, fpu, xmm)
                 RegisterArray callerSaveRegs = getCallerSavedRegisters();
@@ -480,7 +481,7 @@
                 for (int blockId = blocks.length - 1; blockId >= 0; blockId--) {
                     final AbstractBlockBase<?> block = blocks[blockId];
 
-                    try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) {
+                    try (Indent indent2 = debug.logAndIndent("handle block %d", block.getId())) {
                         handleBlockEnd(block, (instructionIndex - 1) << 1);
 
                         /*
@@ -495,7 +496,7 @@
                             numberInstruction(block, op, instructionIndex);
                             final int opId = op.id();
 
-                            try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) {
+                            try (Indent indent3 = debug.logAndIndent("handle inst %d: %s", opId, op)) {
 
                                 /*
                                  * Add a temp range for each register if operation destroys
@@ -507,8 +508,8 @@
                                             addTemp(r.asValue(), opId, RegisterPriority.None);
                                         }
                                     }
-                                    if (Debug.isLogEnabled()) {
-                                        Debug.log("operation destroys all caller-save registers");
+                                    if (debug.isLogEnabled()) {
+                                        debug.log("operation destroys all caller-save registers");
                                     }
                                 }
 
@@ -533,7 +534,7 @@
                         AbstractBlockBase<?> pred = blockId == 0 ? null : blocks[blockId - 1];
                         handleBlockBegin(block, pred);
                     }
-                    if (Debug.isDumpEnabled(Debug.VERY_DETAILED_LEVEL)) {
+                    if (debug.isDumpEnabled(DebugContext.VERY_DETAILED_LEVEL)) {
                         allocator.printIntervals("After Block " + block);
                     }
                 }   // end of block iteration
@@ -615,7 +616,7 @@
 
         @SuppressWarnings("try")
         private void addInterTraceHints() {
-            try (Scope s = Debug.scope("InterTraceHints", allocator)) {
+            try (DebugContext.Scope s = debug.scope("InterTraceHints", allocator)) {
                 GlobalLivenessInfo livenessInfo = allocator.getGlobalLivenessInfo();
                 // set hints for phi/incoming intervals
                 for (AbstractBlockBase<?> block : sortedBlocks()) {
@@ -625,7 +626,7 @@
                     }
                 }
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
 
@@ -656,20 +657,20 @@
             }
             if (isVariableOrRegister(fromValue)) {
                 IntervalHint from = getIntervalHint((AllocatableValue) fromValue);
-                setHint(label, to, from);
+                setHint(label, to, from, debug);
             } else if (isStackSlotValue(fromValue)) {
-                setSpillSlot(label, to, (AllocatableValue) fromValue);
+                setSpillSlot(label, to, (AllocatableValue) fromValue, debug);
             } else if (TraceRAshareSpillInformation.getValue(allocator.getLIR().getOptions()) && isShadowedRegisterValue(fromValue)) {
                 ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue);
                 IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister());
-                setHint(label, to, from);
-                setSpillSlot(label, to, shadowedRegisterValue.getStackSlot());
+                setHint(label, to, from, debug);
+                setSpillSlot(label, to, shadowedRegisterValue.getStackSlot(), debug);
             } else {
                 throw GraalError.shouldNotReachHere();
             }
         }
 
-        private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from) {
+        private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from, DebugContext debug) {
             IntervalHint currentHint = to.locationHint(false);
             if (currentHint == null) {
                 /*
@@ -677,21 +678,21 @@
                  * interval.
                  */
                 to.setLocationHint(from);
-                if (Debug.isLogEnabled()) {
-                    Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
+                if (debug.isLogEnabled()) {
+                    debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
                 }
             }
         }
 
-        private static void setSpillSlot(LIRInstruction op, TraceInterval interval, AllocatableValue spillSlot) {
+        private static void setSpillSlot(LIRInstruction op, TraceInterval interval, AllocatableValue spillSlot, DebugContext debug) {
             if (interval.spillSlot() == null) {
                 interval.setSpillSlot(spillSlot);
                 interval.setSpillState(SpillState.StartInMemory);
-                if (Debug.isLogEnabled()) {
-                    Debug.log("operation at opId %d: added spill slot %s to interval %s", op.id(), spillSlot, interval);
+                if (debug.isLogEnabled()) {
+                    debug.log("operation at opId %d: added spill slot %s to interval %s", op.id(), spillSlot, interval);
                 }
-            } else if (Debug.isLogEnabled()) {
-                Debug.log("operation at opId %d: has already a slot assigned %s", op.id(), interval.spillSlot());
+            } else if (debug.isLogEnabled()) {
+                debug.log("operation at opId %d: has already a slot assigned %s", op.id(), interval.spillSlot());
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -39,8 +39,7 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIR;
@@ -136,6 +135,10 @@
         assert livenessInfo != null;
     }
 
+    protected DebugContext getDebug() {
+        return res.getLIR().getDebug();
+    }
+
     public static boolean isVariableOrRegister(Value value) {
         return isVariable(value) || isRegister(value);
     }
@@ -256,6 +259,10 @@
             return getLIR().getOptions();
         }
 
+        DebugContext getDebug() {
+            return getLIR().getDebug();
+        }
+
         /**
          * Gets the number of operands. This value will increase by 1 for new variable.
          */
@@ -326,12 +333,13 @@
          * {@linkplain TraceInterval variable}.
          */
         private AllocatableValue allocateSpillSlot(TraceInterval interval) {
+            DebugContext debug = res.getLIR().getDebug();
             int variableIndex = interval.splitParent().operandNumber;
             OptionValues options = getOptions();
             if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue(options)) {
                 AllocatableValue cachedStackSlot = cachedStackSlots[variableIndex];
                 if (cachedStackSlot != null) {
-                    TraceRegisterAllocationPhase.globalStackSlots.increment();
+                    TraceRegisterAllocationPhase.globalStackSlots.increment(debug);
                     assert cachedStackSlot.getValueKind().equals(getKind(interval)) : "CachedStackSlot: kind mismatch? " + getKind(interval) + " vs. " + cachedStackSlot.getValueKind();
                     return cachedStackSlot;
                 }
@@ -340,7 +348,7 @@
             if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue(options)) {
                 cachedStackSlots[variableIndex] = slot;
             }
-            TraceRegisterAllocationPhase.allocatedStackSlots.increment();
+            TraceRegisterAllocationPhase.allocatedStackSlots.increment(debug);
             return slot;
         }
 
@@ -531,8 +539,8 @@
             TraceInterval result = interval.getSplitChildAtOpId(opId, mode);
 
             if (result != null) {
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Split child at pos %d of interval %s is %s", opId, interval, result);
+                if (getDebug().isLogEnabled()) {
+                    getDebug().log("Split child at pos %d of interval %s is %s", opId, interval, result);
                 }
                 return result;
             }
@@ -570,10 +578,11 @@
             /*
              * This is the point to enable debug logging for the whole register allocation.
              */
-            try (Indent indent = Debug.logAndIndent("LinearScan allocate")) {
+            DebugContext debug = res.getLIR().getDebug();
+            try (Indent indent = debug.logAndIndent("LinearScan allocate")) {
                 TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE.apply(target, lirGenRes, trace, spillMoveFactory, registerAllocationConfig, traceBuilderResult, this, false);
 
-                try (Scope s = Debug.scope("AfterLifetimeAnalysis", this)) {
+                try (DebugContext.Scope s = debug.scope("AfterLifetimeAnalysis", this)) {
 
                     printLir("After instruction numbering");
                     printIntervals("Before register allocation");
@@ -598,14 +607,14 @@
                         verifyIntervals();
                     }
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
             }
         }
 
         public void printLir(String label) {
-            if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                Debug.dump(Debug.DETAILED_LEVEL, sortedBlocks(), "%s (Trace%d)", label, trace.getId());
+            if (getDebug().isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                getDebug().dump(DebugContext.DETAILED_LEVEL, sortedBlocks(), "%s (Trace%d)", label, trace.getId());
             }
         }
 
@@ -616,7 +625,7 @@
 
             verifyRegisters();
 
-            Debug.log("no errors found");
+            getDebug().log("no errors found");
 
             return true;
         }
@@ -624,7 +633,7 @@
         @SuppressWarnings("try")
         private void verifyRegisters() {
             // Enable this logging to get output for the verification process.
-            try (Indent indent = Debug.logAndIndent("verifying register allocation")) {
+            try (Indent indent = getDebug().logAndIndent("verifying register allocation")) {
                 RegisterVerifier verifier = new RegisterVerifier(this);
                 verifier.verify(blockAt(0));
             }
@@ -632,7 +641,8 @@
 
         @SuppressWarnings("try")
         protected void verifyIntervals() {
-            try (Indent indent = Debug.logAndIndent("verifying intervals")) {
+            DebugContext debug = getDebug();
+            try (Indent indent = debug.logAndIndent("verifying intervals")) {
                 int len = intervalsSize();
 
                 for (int i = 0; i < len; i++) {
@@ -644,32 +654,32 @@
                     i1.checkSplitChildren();
 
                     if (i1.operandNumber != i) {
-                        Debug.log("Interval %d is on position %d in list", i1.operandNumber, i);
-                        Debug.log(i1.logString());
+                        debug.log("Interval %d is on position %d in list", i1.operandNumber, i);
+                        debug.log(i1.logString());
                         throw new GraalError("");
                     }
 
                     if (getKind(i1).equals(LIRKind.Illegal)) {
-                        Debug.log("Interval %d has no type assigned", i1.operandNumber);
-                        Debug.log(i1.logString());
+                        debug.log("Interval %d has no type assigned", i1.operandNumber);
+                        debug.log(i1.logString());
                         throw new GraalError("");
                     }
 
                     if (i1.location() == null) {
-                        Debug.log("Interval %d has no register assigned", i1.operandNumber);
-                        Debug.log(i1.logString());
+                        debug.log("Interval %d has no register assigned", i1.operandNumber);
+                        debug.log(i1.logString());
                         throw new GraalError("");
                     }
 
                     if (i1.isEmpty()) {
-                        Debug.log("Interval %d has no Range", i1.operandNumber);
-                        Debug.log(i1.logString());
+                        debug.log("Interval %d has no Range", i1.operandNumber);
+                        debug.log(i1.logString());
                         throw new GraalError("");
                     }
 
                     if (i1.from() >= i1.to()) {
-                        Debug.log("Interval %d has zero length range", i1.operandNumber);
-                        Debug.log(i1.logString());
+                        debug.log("Interval %d has zero length range", i1.operandNumber);
+                        debug.log(i1.logString());
                         throw new GraalError("");
                     }
 
@@ -958,29 +968,30 @@
 
         @SuppressWarnings("try")
         public void printIntervals(String label) {
-            if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                if (Debug.isLogEnabled()) {
-                    try (Indent indent = Debug.logAndIndent("intervals %s", label)) {
+            DebugContext debug = getDebug();
+            if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                if (debug.isLogEnabled()) {
+                    try (Indent indent = debug.logAndIndent("intervals %s", label)) {
                         for (FixedInterval interval : fixedIntervals) {
                             if (interval != null) {
-                                Debug.log("%s", interval.logString());
+                                debug.log("%s", interval.logString());
                             }
                         }
 
                         for (TraceInterval interval : intervals) {
                             if (interval != null) {
-                                Debug.log("%s", interval.logString());
+                                debug.log("%s", interval.logString());
                             }
                         }
 
-                        try (Indent indent2 = Debug.logAndIndent("Basic Blocks")) {
+                        try (Indent indent2 = debug.logAndIndent("Basic Blocks")) {
                             for (AbstractBlockBase<?> block : trace.getBlocks()) {
-                                Debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop());
+                                debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop());
                             }
                         }
                     }
                 }
-                Debug.dump(Debug.DETAILED_LEVEL, this, "%s (Trace%d)", label, trace.getId());
+                debug.dump(DebugContext.DETAILED_LEVEL, this, "%s (Trace%d)", label, trace.getId());
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanRegisterAllocationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanRegisterAllocationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
@@ -43,7 +43,8 @@
 
     @SuppressWarnings("try")
     private static void allocateRegisters(TraceLinearScan allocator) {
-        try (Indent indent = Debug.logAndIndent("allocate registers")) {
+        DebugContext debug = allocator.getDebug();
+        try (Indent indent = debug.logAndIndent("allocate registers")) {
             FixedInterval precoloredIntervals = allocator.createFixedUnhandledList();
             TraceInterval notPrecoloredIntervals = allocator.createUnhandledListByFrom(TraceLinearScanPhase.IS_VARIABLE_INTERVAL);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,8 +36,8 @@
 import org.graalvm.compiler.core.common.alloc.Trace;
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -69,16 +69,18 @@
     private static final class Resolver {
         private final TraceLinearScan allocator;
         private final TraceBuilderResult traceBuilderResult;
+        private final DebugContext debug;
 
         private Resolver(TraceLinearScan allocator, TraceBuilderResult traceBuilderResult) {
             this.allocator = allocator;
             this.traceBuilderResult = traceBuilderResult;
+            this.debug = allocator.getDebug();
         }
 
         private void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) {
             if (fromBlock.getSuccessorCount() <= 1) {
-                if (Debug.isLogEnabled()) {
-                    Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
+                if (debug.isLogEnabled()) {
+                    debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
                 }
 
                 ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(fromBlock);
@@ -91,8 +93,8 @@
                 }
 
             } else {
-                if (Debug.isLogEnabled()) {
-                    Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
+                if (debug.isLogEnabled()) {
+                    debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
                 }
 
                 if (DetailedAsserts.getValue(allocator.getOptions())) {
@@ -123,7 +125,7 @@
                 // no resolution necessary
                 return;
             }
-            try (Indent indent = Debug.logAndIndent("resolve data flow")) {
+            try (Indent indent = debug.logAndIndent("resolve data flow")) {
 
                 TraceLocalMoveResolver moveResolver = allocator.createMoveResolver();
                 AbstractBlockBase<?> toBlock = null;
@@ -148,7 +150,7 @@
 
         @SuppressWarnings("try")
         private void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) {
-            try (Indent indent0 = Debug.logAndIndent("Edge %s -> %s", fromBlock, toBlock)) {
+            try (Indent indent0 = debug.logAndIndent("Edge %s -> %s", fromBlock, toBlock)) {
                 // collect all intervals that have been split between
                 // fromBlock and toBlock
                 int toId = allocator.getFirstLirInstructionId(toBlock);
@@ -180,8 +182,8 @@
             return currentTrace.getId() == traceBuilderResult.getTraceForBlock(block).getId();
         }
 
-        private static final DebugCounter numResolutionMoves = Debug.counter("TraceRA[numTraceLSRAResolutionMoves]");
-        private static final DebugCounter numStackToStackMoves = Debug.counter("TraceRA[numTraceLSRAStackToStackMoves]");
+        private static final CounterKey numResolutionMoves = DebugContext.counter("TraceRA[numTraceLSRAResolutionMoves]");
+        private static final CounterKey numStackToStackMoves = DebugContext.counter("TraceRA[numTraceLSRAStackToStackMoves]");
 
         private void addMapping(Value phiFrom, Value phiTo, int fromId, int toId, TraceLocalMoveResolver moveResolver) {
             assert !isRegister(phiFrom) : "Out is a register: " + phiFrom;
@@ -193,7 +195,7 @@
             }
             TraceInterval toParent = allocator.intervalFor(asVariable(phiTo));
             if (isConstantValue(phiFrom)) {
-                numResolutionMoves.increment();
+                numResolutionMoves.increment(debug);
                 TraceInterval toInterval = allocator.splitChildAtOpId(toParent, toId, LIRInstruction.OperandMode.DEF);
                 moveResolver.addMapping(asConstant(phiFrom), toInterval);
             } else {
@@ -209,9 +211,9 @@
                 return;
             }
             if (fromInterval != toInterval) {
-                numResolutionMoves.increment();
-                if (numStackToStackMoves.isEnabled() && isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location())) {
-                    numStackToStackMoves.increment();
+                numResolutionMoves.increment(debug);
+                if (isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location())) {
+                    numStackToStackMoves.increment(debug);
                 }
                 moveResolver.addMapping(fromInterval, toInterval);
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -35,7 +35,7 @@
 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.AllocatableRegisters;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.util.Util;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInstruction;
@@ -175,6 +175,7 @@
     private int maxReg;
 
     private final TraceLinearScan allocator;
+    private final DebugContext debug;
 
     /**
      * Sorted list of intervals, not live before the current position.
@@ -222,6 +223,7 @@
 
     TraceLinearScanWalker(TraceLinearScan allocator, FixedInterval unhandledFixedFirst, TraceInterval unhandledAnyFirst) {
         this.allocator = allocator;
+        this.debug = allocator.getDebug();
 
         unhandledAnyList = unhandledAnyFirst;
         activeAnyList = TraceInterval.EndMarker;
@@ -466,8 +468,8 @@
     @SuppressWarnings({"unused"})
     private int findOptimalSplitPos(TraceInterval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) {
         int optimalSplitPos = findOptimalSplitPos0(minSplitPos, maxSplitPos);
-        if (Debug.isLogEnabled()) {
-            Debug.log("optimal split position: %d", optimalSplitPos);
+        if (debug.isLogEnabled()) {
+            debug.log("optimal split position: %d", optimalSplitPos);
         }
         return optimalSplitPos;
     }
@@ -475,8 +477,8 @@
     private int findOptimalSplitPos0(int minSplitPos, int maxSplitPos) {
         if (minSplitPos == maxSplitPos) {
             // trivial case, no optimization of split position possible
-            if (Debug.isLogEnabled()) {
-                Debug.log("min-pos and max-pos are equal, no optimization possible");
+            if (debug.isLogEnabled()) {
+                debug.log("min-pos and max-pos are equal, no optimization possible");
             }
             return minSplitPos;
 
@@ -499,15 +501,15 @@
         assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order";
         if (minBlock == maxBlock) {
             // split position cannot be moved to block boundary : so split as late as possible
-            if (Debug.isLogEnabled()) {
-                Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
+            if (debug.isLogEnabled()) {
+                debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
             }
             return maxSplitPos;
 
         }
         // seach optimal block boundary between minSplitPos and maxSplitPos
-        if (Debug.isLogEnabled()) {
-            Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
+        if (debug.isLogEnabled()) {
+            debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
         }
 
         return findOptimalSplitPos(minBlock, maxBlock, maxSplitPos);
@@ -520,7 +522,7 @@
     @SuppressWarnings("try")
     private void splitBeforeUsage(TraceInterval interval, int minSplitPos, int maxSplitPos) {
 
-        try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
+        try (Indent indent = debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
 
             assert interval.from() < minSplitPos : "cannot split at start of interval";
             assert currentPosition < minSplitPos : "cannot split before current position";
@@ -532,8 +534,8 @@
             if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) {
                 // the split position would be just before the end of the interval
                 // . no split at all necessary
-                if (Debug.isLogEnabled()) {
-                    Debug.log("no split necessary because optimal split position is at end of interval");
+                if (debug.isLogEnabled()) {
+                    debug.log("no split necessary because optimal split position is at end of interval");
                 }
                 return;
             }
@@ -555,8 +557,8 @@
             assert optimalSplitPosFinal <= interval.to() : "cannot split after end of interval";
             assert optimalSplitPosFinal > interval.from() : "cannot split at start of interval";
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("splitting at position %d", optimalSplitPosFinal);
+            if (debug.isLogEnabled()) {
+                debug.log("splitting at position %d", optimalSplitPosFinal);
             }
             assert optimalSplitPosFinal > currentPosition : "Can not split interval " + interval + " at current position: " + currentPosition;
 
@@ -567,8 +569,8 @@
             if (optimalSplitPosFinal == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) {
                 // the split position would be just before the end of the interval
                 // . no split at all necessary
-                if (Debug.isLogEnabled()) {
-                    Debug.log("no split necessary because optimal split position is at end of interval");
+                if (debug.isLogEnabled()) {
+                    debug.log("no split necessary because optimal split position is at end of interval");
                 }
                 return;
             }
@@ -579,9 +581,9 @@
             assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position";
             unhandledAnyList = TraceLinearScanWalker.addToListSortedByStartAndUsePositions(unhandledAnyList, splitPart);
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("left interval  %s: %s", moveNecessary ? "      " : "", interval.logString());
-                Debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString());
+            if (debug.isLogEnabled()) {
+                debug.log("left interval  %s: %s", moveNecessary ? "      " : "", interval.logString());
+                debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString());
             }
         }
     }
@@ -605,7 +607,7 @@
         }
         int minSplitPos = Math.max(previousUsage + 1, interval.from());
 
-        try (Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
+        try (Indent indent = debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
 
             assert interval.from() <= minSplitPos : "cannot split before start of interval";
             assert minSplitPos <= maxSplitPos : "invalid order";
@@ -615,7 +617,7 @@
             if (minSplitPos == interval.from()) {
                 // the whole interval is never used, so spill it entirely to memory
 
-                try (Indent indent2 = Debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.numUsePos())) {
+                try (Indent indent2 = debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.numUsePos())) {
 
                     assert interval.firstUsage(RegisterPriority.MustHaveRegister) > currentPosition : String.format("interval %s must not have use position before currentPosition %d", interval,
                                     currentPosition);
@@ -634,8 +636,8 @@
                         if (isRegister(parent.location())) {
                             if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) {
                                 // parent is never used, so kick it out of its assigned register
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber);
+                                if (debug.isLogEnabled()) {
+                                    debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber);
                                 }
                                 allocator.assignSpillSlot(parent);
                                 handleSpillSlot(parent);
@@ -661,7 +663,7 @@
                     optimalSplitPos = (optimalSplitPos - 1) | 1;
                 }
 
-                try (Indent indent2 = Debug.logAndIndent("splitting at position %d", optimalSplitPos)) {
+                try (Indent indent2 = debug.logAndIndent("splitting at position %d", optimalSplitPos)) {
                     assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary";
                     assert !allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 0) : "split pos must be even on block boundary";
 
@@ -671,13 +673,13 @@
                     changeSpillState(spilledPart, optimalSplitPos);
 
                     if (!allocator.isBlockBegin(optimalSplitPos)) {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("inserting move from interval %s to %s", interval, spilledPart);
+                        if (debug.isLogEnabled()) {
+                            debug.log("inserting move from interval %s to %s", interval, spilledPart);
                         }
                         insertMove(optimalSplitPos, interval, spilledPart);
                     } else {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("no need to insert move. done by data-flow resolution");
+                        if (debug.isLogEnabled()) {
+                            debug.log("no need to insert move. done by data-flow resolution");
                         }
                     }
 
@@ -685,9 +687,9 @@
                     assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild";
                     spilledPart.makeCurrentSplitChild();
 
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("left interval: %s", interval.logString());
-                        Debug.log("spilled interval   : %s", spilledPart.logString());
+                    if (debug.isLogEnabled()) {
+                        debug.log("left interval: %s", interval.logString());
+                        debug.log("spilled interval   : %s", spilledPart.logString());
                     }
                 }
             }
@@ -795,8 +797,8 @@
      */
     private int findOptimalSpillPos(int minSpillPos, int maxSpillPos) {
         int optimalSpillPos = findOptimalSpillPos0(minSpillPos, maxSpillPos) & (~1);
-        if (Debug.isLogEnabled()) {
-            Debug.log("optimal spill position: %d", optimalSpillPos);
+        if (debug.isLogEnabled()) {
+            debug.log("optimal spill position: %d", optimalSpillPos);
         }
         return optimalSpillPos;
     }
@@ -804,8 +806,8 @@
     private int findOptimalSpillPos0(int minSpillPos, int maxSpillPos) {
         if (minSpillPos == maxSpillPos) {
             // trivial case, no optimization of split position possible
-            if (Debug.isLogEnabled()) {
-                Debug.log("min-pos and max-pos are equal, no optimization possible");
+            if (debug.isLogEnabled()) {
+                debug.log("min-pos and max-pos are equal, no optimization possible");
             }
             return minSpillPos;
 
@@ -819,15 +821,15 @@
         assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order";
         if (minBlock == maxBlock) {
             // split position cannot be moved to block boundary : so split as late as possible
-            if (Debug.isLogEnabled()) {
-                Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
+            if (debug.isLogEnabled()) {
+                debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block");
             }
             return maxSpillPos;
 
         }
         // search optimal block boundary between minSplitPos and maxSplitPos
-        if (Debug.isLogEnabled()) {
-            Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
+        if (debug.isLogEnabled()) {
+            debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
         }
 
         // currently using the same heuristic as for splitting
@@ -910,7 +912,7 @@
         if (maxSplitPos <= interval.to()) {
             splitBeforeUsage(interval, minSplitPos, maxSplitPos);
         } else {
-            Debug.log("No more usage, no need to split: %s", interval);
+            debug.log("No more usage, no need to split: %s", interval);
         }
 
         assert interval.nextUsage(RegisterPriority.MustHaveRegister, currentPos) == Integer.MAX_VALUE : "the remaining part is spilled to stack and therefore has no register";
@@ -919,7 +921,7 @@
 
     @SuppressWarnings("try")
     private boolean allocFreeRegister(TraceInterval interval) {
-        try (Indent indent = Debug.logAndIndent("trying to find free register for %s", interval)) {
+        try (Indent indent = debug.logAndIndent("trying to find free register for %s", interval)) {
 
             initUseLists(true);
             freeExcludeActiveFixed();
@@ -931,12 +933,12 @@
             // (either as a fixed register or a normal allocated register in the past)
             // only intervals overlapping with cur are processed, non-overlapping invervals can be
             // ignored safely
-            if (Debug.isLogEnabled()) {
+            if (debug.isLogEnabled()) {
                 // Enable this logging to see all register states
-                try (Indent indent2 = Debug.logAndIndent("state of registers:")) {
+                try (Indent indent2 = debug.logAndIndent("state of registers:")) {
                     for (Register register : availableRegs) {
                         int i = register.number;
-                        Debug.log("reg %d (%s): usePos: %d", register.number, register, usePos[i]);
+                        debug.log("reg %d (%s): usePos: %d", register.number, register, usePos[i]);
                     }
                 }
             }
@@ -945,8 +947,8 @@
             IntervalHint locationHint = interval.locationHint(true);
             if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) {
                 hint = asRegister(locationHint.location());
-                if (Debug.isLogEnabled()) {
-                    Debug.log("hint register %3d (%4s) from interval %s", hint.number, hint, locationHint);
+                if (debug.isLogEnabled()) {
+                    debug.log("hint register %3d (%4s) from interval %s", hint.number, hint, locationHint);
                 }
             }
             assert interval.location() == null : "register already assigned to interval";
@@ -988,8 +990,8 @@
 
             splitPos = usePos[reg.number];
             interval.assignLocation(reg.asValue(allocator.getKind(interval)));
-            if (Debug.isLogEnabled()) {
-                Debug.log("selected register %d (%s)", reg.number, reg);
+            if (debug.isLogEnabled()) {
+                debug.log("selected register %d (%s)", reg.number, reg);
             }
 
             assert splitPos > 0 : "invalid splitPos";
@@ -1015,7 +1017,7 @@
     // Split an Interval and spill it to memory so that cur can be placed in a register
     @SuppressWarnings("try")
     private void allocLockedRegister(TraceInterval interval) {
-        try (Indent indent = Debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) {
+        try (Indent indent = debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) {
 
             // the register must be free at least until this position
             int firstUsage = interval.firstUsage(RegisterPriority.MustHaveRegister);
@@ -1039,7 +1041,7 @@
                 // spillBlockUnhandledFixed(cur);
                 spillBlockInactiveFixed(interval);
                 spillCollectActiveAny(registerPriority);
-                if (Debug.isLogEnabled()) {
+                if (debug.isLogEnabled()) {
                     printRegisterState();
                 }
 
@@ -1061,16 +1063,16 @@
                     }
                 }
 
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Register Selected: %s", reg);
+                if (debug.isLogEnabled()) {
+                    debug.log("Register Selected: %s", reg);
                 }
 
                 int regUsePos = (reg == null ? 0 : usePos[reg.number]);
                 if (regUsePos <= firstShouldHaveUsage) {
                     /* Check if there is another interval that is already in memory. */
                     if (reg == null || interval.inMemoryAt(currentPosition) || !isInMemory.get(reg.number)) {
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos);
+                        if (debug.isLogEnabled()) {
+                            debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos);
                         }
 
                         if (firstUsage <= interval.from() + 1) {
@@ -1080,7 +1082,7 @@
                                  * try to spill an active interval that has a usage but do not
                                  * require a register.
                                  */
-                                Debug.log("retry with register priority must have register");
+                                debug.log("retry with register priority must have register");
                                 continue;
                             }
                             String description = "cannot spill interval (" + interval + ") that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage +
@@ -1090,7 +1092,7 @@
                              * avoid errors
                              */
                             allocator.assignSpillSlot(interval);
-                            if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
+                            if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
                                 dumpLIRAndIntervals(description);
                             }
                             throw new OutOfRegistersException("LinearScan: no register found", description);
@@ -1108,8 +1110,8 @@
 
             int splitPos = blockPos[reg.number];
 
-            if (Debug.isLogEnabled()) {
-                Debug.log("decided to use register %d", reg.number);
+            if (debug.isLogEnabled()) {
+                debug.log("decided to use register %d", reg.number);
             }
             assert splitPos > 0 : "invalid splitPos";
             assert needSplit || splitPos > interval.from() : "splitting interval at from";
@@ -1127,18 +1129,18 @@
     }
 
     private void dumpLIRAndIntervals(String description) {
-        Debug.dump(Debug.INFO_LEVEL, allocator.getLIR(), description);
+        debug.dump(DebugContext.INFO_LEVEL, allocator.getLIR(), description);
         allocator.printIntervals(description);
     }
 
     @SuppressWarnings("try")
     private void printRegisterState() {
-        try (Indent indent2 = Debug.logAndIndent("state of registers:")) {
+        try (Indent indent2 = debug.logAndIndent("state of registers:")) {
             for (Register reg : availableRegs) {
                 int i = reg.number;
-                try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, inMemory: %b, intervals: ", i, usePos[i], blockPos[i], isInMemory.get(i))) {
+                try (Indent indent3 = debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, inMemory: %b, intervals: ", i, usePos[i], blockPos[i], isInMemory.get(i))) {
                     for (int j = 0; j < spillIntervals[i].size(); j++) {
-                        Debug.log("%s", spillIntervals[i].get(j));
+                        debug.log("%s", spillIntervals[i].get(j));
                     }
                 }
             }
@@ -1157,8 +1159,8 @@
             if (isOdd(pos)) {
                 // the current instruction is a call that blocks all registers
                 if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) {
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("free register cannot be available because all registers blocked by following call");
+                    if (debug.isLogEnabled()) {
+                        debug.log("free register cannot be available because all registers blocked by following call");
                     }
 
                     // safety check that there is really no register available
@@ -1250,19 +1252,19 @@
     // allocate a physical register or memory location to an interval
     @SuppressWarnings("try")
     private boolean activateCurrent(TraceInterval interval) {
-        if (Debug.isLogEnabled()) {
+        if (debug.isLogEnabled()) {
             logCurrentStatus();
         }
         boolean result = true;
 
-        try (Indent indent = Debug.logAndIndent("activating interval %s,  splitParent: %d", interval, interval.splitParent().operandNumber)) {
+        try (Indent indent = debug.logAndIndent("activating interval %s,  splitParent: %d", interval, interval.splitParent().operandNumber)) {
 
             if (interval.location() != null && isStackSlotValue(interval.location())) {
                 // activating an interval that has a stack slot assigned . split it at first use
                 // position
                 // used for method parameters
-                if (Debug.isLogEnabled()) {
-                    Debug.log("interval has spill slot assigned (method parameter) . split it before first use");
+                if (debug.isLogEnabled()) {
+                    debug.log("interval has spill slot assigned (method parameter) . split it before first use");
                 }
                 splitStackInterval(interval);
                 result = false;
@@ -1271,8 +1273,8 @@
                 if (interval.location() == null) {
                     // interval has not assigned register . normal allocation
                     // (this is the normal case for most intervals)
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("normal allocation of register");
+                    if (debug.isLogEnabled()) {
+                        debug.log("normal allocation of register");
                     }
 
                     // assign same spill slot to non-intersecting intervals
@@ -1297,8 +1299,8 @@
                 assert interval.isSplitChild();
                 assert interval.currentSplitChild() != null;
                 assert interval.currentSplitChild().operandNumber != interval.operandNumber : "cannot insert move between same interval";
-                if (Debug.isLogEnabled()) {
-                    Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber);
+                if (debug.isLogEnabled()) {
+                    debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber);
                 }
 
                 insertMove(interval.from(), interval.currentSplitChild(), interval);
@@ -1317,12 +1319,12 @@
 
     @SuppressWarnings("try")
     private void logCurrentStatus() {
-        try (Indent i = Debug.logAndIndent("active:")) {
-            logList(activeFixedList);
-            logList(activeAnyList);
+        try (Indent i = debug.logAndIndent("active:")) {
+            logList(debug, activeFixedList);
+            logList(debug, activeAnyList);
         }
-        try (Indent i = Debug.logAndIndent("inactive(fixed):")) {
-            logList(inactiveFixedList);
+        try (Indent i = debug.logAndIndent("inactive(fixed):")) {
+            logList(debug, inactiveFixedList);
         }
     }
 
@@ -1347,9 +1349,9 @@
         FixedInterval prevprev = null;
         FixedInterval prev = (state == State.Active) ? activeFixedList : inactiveFixedList;
         FixedInterval next = prev;
-        if (Debug.isLogEnabled()) {
-            try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) {
-                logList(next);
+        if (debug.isLogEnabled()) {
+            try (Indent i = debug.logAndIndent("walkToFixed(%s, %d):", state, from)) {
+                logList(debug, next);
             }
         }
         while (next.currentFrom() <= from) {
@@ -1397,7 +1399,7 @@
                         prev = cur.next;
                     }
                 }
-                intervalMoved(cur, state, newState);
+                intervalMoved(debug, cur, state, newState);
             } else {
                 prevprev = prev;
                 prev = cur.next;
@@ -1416,9 +1418,9 @@
         TraceInterval prevprev = null;
         TraceInterval prev = activeAnyList;
         TraceInterval next = prev;
-        if (Debug.isLogEnabled()) {
-            try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) {
-                logList(next);
+        if (debug.isLogEnabled()) {
+            try (Indent i = debug.logAndIndent("walkToAny(%d):", from)) {
+                logList(debug, next);
             }
         }
         while (next.from() <= from) {
@@ -1432,7 +1434,7 @@
                 } else {
                     prevprev.next = next;
                 }
-                intervalMoved(cur, State.Active, State.Handled);
+                intervalMoved(debug, cur, State.Active, State.Handled);
             } else {
                 prevprev = prev;
             }
@@ -1489,10 +1491,10 @@
             walkToFixed(State.Inactive, opId);
             walkToAny(opId);
 
-            try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) {
+            try (Indent indent = debug.logAndIndent("walk to op %d", opId)) {
                 if (activateCurrent(currentInterval)) {
                     activeAnyList = TraceLinearScanWalker.addToListSortedByFromPositions(activeAnyList, currentInterval);
-                    intervalMoved(currentInterval, State.Unhandled, State.Active);
+                    intervalMoved(debug, currentInterval, State.Unhandled, State.Active);
                 }
             }
         }
@@ -1510,23 +1512,23 @@
         }
     }
 
-    private static void logList(FixedInterval i) {
+    private static void logList(DebugContext debug, FixedInterval i) {
         for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) {
-            Debug.log("%s", interval.logString());
+            debug.log("%s", interval.logString());
         }
     }
 
-    private static void logList(TraceInterval i) {
+    private static void logList(DebugContext debug, TraceInterval i) {
         for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) {
-            Debug.log("%s", interval.logString());
+            debug.log("%s", interval.logString());
         }
     }
 
-    private static void intervalMoved(IntervalHint interval, State from, State to) {
+    private static void intervalMoved(DebugContext debug, IntervalHint interval, State from, State to) {
         // intervalMoved() is called whenever an interval moves from one interval list to another.
         // In the implementation of this method it is prohibited to move the interval to any list.
-        if (Debug.isLogEnabled()) {
-            Debug.log("interval moved from %s to %s: %s", from, to, interval.logString());
+        if (debug.isLogEnabled()) {
+            debug.log("interval moved from %s to %s: %s", from, to, interval.logString());
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLocalMoveResolver.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLocalMoveResolver.java	Fri Jul 07 09:40:47 2017 -0700
@@ -37,8 +37,8 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.LIRKind;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
@@ -54,11 +54,9 @@
 import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.Value;
 
-/**
- */
 final class TraceLocalMoveResolver {
 
-    private static final DebugCounter cycleBreakingSlotsAllocated = Debug.counter("TraceRA[cycleBreakingSlotsAllocated(local)]");
+    private static final CounterKey cycleBreakingSlotsAllocated = DebugContext.counter("TraceRA[cycleBreakingSlotsAllocated(local)]");
 
     private static final int STACK_SLOT_IN_CALLER_FRAME_IDX = -1;
     private final TraceLinearScan allocator;
@@ -74,6 +72,8 @@
     private int[] stackBlocked;
     private final int firstVirtualStackIndex;
 
+    private final DebugContext debug;
+
     private int getStackArrayIndex(Value stackSlotValue) {
         if (isStackSlot(stackSlotValue)) {
             return getStackArrayIndex(asStackSlot(stackSlotValue));
@@ -168,6 +168,7 @@
     protected TraceLocalMoveResolver(TraceLinearScan allocator) {
 
         this.allocator = allocator;
+        this.debug = allocator.getDebug();
         this.mappingFrom = new ArrayList<>(8);
         this.mappingFromOpr = new ArrayList<>(8);
         this.mappingTo = new ArrayList<>(8);
@@ -256,7 +257,7 @@
             assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
             int direction = 1;
             setValueBlocked(location, direction);
-            Debug.log("block %s", location);
+            debug.log("block %s", location);
         }
     }
 
@@ -266,7 +267,7 @@
         if (mightBeBlocked(location)) {
             assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
             setValueBlocked(location, -1);
-            Debug.log("unblock %s", location);
+            debug.log("unblock %s", location);
         }
     }
 
@@ -329,8 +330,8 @@
 
         insertionBuffer.append(insertIdx, createMove(allocator.getOperand(fromInterval), allocator.getOperand(toInterval), fromInterval.location(), toInterval.location()));
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
+        if (debug.isLogEnabled()) {
+            debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
         }
     }
 
@@ -354,16 +355,16 @@
         LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr);
         insertionBuffer.append(insertIdx, move);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
+        if (debug.isLogEnabled()) {
+            debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
         }
     }
 
     @SuppressWarnings("try")
     private void resolveMappings() {
-        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
+        try (Indent indent = debug.logAndIndent("resolveMapping")) {
             assert verifyBeforeResolve();
-            if (Debug.isLogEnabled()) {
+            if (debug.isLogEnabled()) {
                 printMapping();
             }
 
@@ -438,7 +439,7 @@
             if (spillSlot1 == null) {
                 spillSlot1 = getAllocator().getFrameMapBuilder().allocateSpillSlot(allocator.getKind(fromInterval1));
                 fromInterval1.setSpillSlot(spillSlot1);
-                cycleBreakingSlotsAllocated.increment();
+                cycleBreakingSlotsAllocated.increment(debug);
             }
             spillInterval(spillCandidate, fromInterval1, spillSlot1);
             return;
@@ -463,8 +464,8 @@
 
         spillInterval.assignLocation(spillSlot);
 
-        if (Debug.isLogEnabled()) {
-            Debug.log("created new Interval for spilling: %s", spillInterval);
+        if (debug.isLogEnabled()) {
+            debug.log("created new Interval for spilling: %s", spillInterval);
         }
         blockRegisters(spillInterval);
 
@@ -476,7 +477,7 @@
 
     @SuppressWarnings("try")
     private void printMapping() {
-        try (Indent indent = Debug.logAndIndent("Mapping")) {
+        try (Indent indent = debug.logAndIndent("Mapping")) {
             for (int i = mappingFrom.size() - 1; i >= 0; i--) {
                 TraceInterval fromInterval = mappingFrom.get(i);
                 TraceInterval toInterval = mappingTo.get(i);
@@ -487,7 +488,7 @@
                 } else {
                     from = fromInterval.location().toString();
                 }
-                Debug.log("move %s <- %s", from, to);
+                debug.log("move %s <- %s", from, to);
             }
         }
     }
@@ -520,8 +521,8 @@
     public void addMapping(TraceInterval fromInterval, TraceInterval toInterval) {
 
         if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("no store to rematerializable interval %s needed", toInterval);
+            if (debug.isLogEnabled()) {
+                debug.log("no store to rematerializable interval %s needed", toInterval);
             }
             return;
         }
@@ -531,8 +532,8 @@
             addMapping(rematValue, toInterval);
             return;
         }
-        if (Debug.isLogEnabled()) {
-            Debug.log("add move mapping from %s to %s", fromInterval, toInterval);
+        if (debug.isLogEnabled()) {
+            debug.log("add move mapping from %s to %s", fromInterval, toInterval);
         }
 
         assert fromInterval.operandNumber != toInterval.operandNumber : "from and to interval equal: " + fromInterval;
@@ -544,8 +545,8 @@
     }
 
     public void addMapping(Constant fromOpr, TraceInterval toInterval) {
-        if (Debug.isLogEnabled()) {
-            Debug.log("add move mapping from %s to %s", fromOpr, toInterval);
+        if (debug.isLogEnabled()) {
+            debug.log("add move mapping from %s to %s", fromOpr, toInterval);
         }
 
         mappingFrom.add(null);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,16 +34,16 @@
 
 import org.graalvm.compiler.asm.AbstractAddress;
 import org.graalvm.compiler.asm.Assembler;
-import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.code.CompilationResult.CodeAnnotation;
 import org.graalvm.compiler.code.DataSection.Data;
 import org.graalvm.compiler.code.DataSection.RawData;
+import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
 import org.graalvm.compiler.core.common.type.DataPointerConstant;
 import org.graalvm.compiler.debug.Assertions;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.lir.LIR;
@@ -145,18 +145,19 @@
     private List<ExceptionInfo> exceptionInfoList;
 
     private final OptionValues options;
+    private final DebugContext debug;
     private final EconomicMap<Constant, Data> dataCache;
 
     private Consumer<LIRInstruction> beforeOp;
     private Consumer<LIRInstruction> afterOp;
 
     public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext,
-                    OptionValues options, CompilationResult compilationResult) {
-        this(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, compilationResult, EconomicMap.create(Equivalence.DEFAULT));
+                    OptionValues options, DebugContext debug, CompilationResult compilationResult) {
+        this(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, debug, compilationResult, EconomicMap.create(Equivalence.DEFAULT));
     }
 
     public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext,
-                    OptionValues options, CompilationResult compilationResult, EconomicMap<Constant, Data> dataCache) {
+                    OptionValues options, DebugContext debug, CompilationResult compilationResult, EconomicMap<Constant, Data> dataCache) {
         this.target = codeCache.getTarget();
         this.codeCache = codeCache;
         this.foreignCalls = foreignCalls;
@@ -166,6 +167,7 @@
         this.compilationResult = compilationResult;
         this.frameContext = frameContext;
         this.options = options;
+        this.debug = debug;
         assert frameContext != null;
         this.dataCache = dataCache;
 
@@ -263,7 +265,7 @@
     public void recordInlineDataInCode(Constant data) {
         assert data != null;
         int pos = asm.position();
-        Debug.log("Inline data in code: pos = %d, data = %s", pos, data);
+        debug.log("Inline data in code: pos = %d, data = %s", pos, data);
         if (data instanceof VMConstant) {
             compilationResult.recordDataPatch(pos, new ConstantReference((VMConstant) data));
         }
@@ -272,7 +274,7 @@
     public void recordInlineDataInCodeWithNote(Constant data, Object note) {
         assert data != null;
         int pos = asm.position();
-        Debug.log("Inline data in code: pos = %d, data = %s, note = %s", pos, data, note);
+        debug.log("Inline data in code: pos = %d, data = %s, note = %s", pos, data, note);
         if (data instanceof VMConstant) {
             compilationResult.recordDataPatchWithNote(pos, new ConstantReference((VMConstant) data), note);
         }
@@ -292,7 +294,7 @@
 
     public AbstractAddress recordDataReferenceInCode(Constant constant, int alignment) {
         assert constant != null;
-        Debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant);
+        debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant);
         Data data = dataCache.get(constant);
         if (data == null) {
             data = dataBuilder.createDataItem(constant);
@@ -304,8 +306,8 @@
 
     public AbstractAddress recordDataReferenceInCode(byte[] data, int alignment) {
         assert data != null;
-        if (Debug.isLogEnabled()) {
-            Debug.log("Data reference in code: pos = %d, data = %s", asm.position(), Arrays.toString(data));
+        if (debug.isLogEnabled()) {
+            debug.log("Data reference in code: pos = %d, data = %s", asm.position(), Arrays.toString(data));
         }
         return recordDataSectionReference(new RawData(data, alignment));
     }
@@ -461,7 +463,7 @@
         if (block == null) {
             return;
         }
-        boolean emitComment = Debug.isDumpEnabled(Debug.BASIC_LEVEL) || PrintLIRWithAssembly.getValue(getOptions());
+        boolean emitComment = debug.isDumpEnabled(DebugContext.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/asm/CompilationResultBuilderFactory.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilderFactory.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,6 +25,7 @@
 import org.graalvm.compiler.asm.Assembler;
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.framemap.FrameMap;
 import org.graalvm.compiler.options.OptionValues;
 
@@ -39,7 +40,7 @@
      * Creates a new {@link CompilationResultBuilder}.
      */
     CompilationResultBuilder createBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext,
-                    OptionValues options, CompilationResult compilationResult);
+                    OptionValues options, DebugContext debug, CompilationResult compilationResult);
 
     /**
      * The default factory creates a standard {@link CompilationResultBuilder}.
@@ -48,8 +49,8 @@
 
         @Override
         public CompilationResultBuilder createBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder,
-                        FrameContext frameContext, OptionValues options, CompilationResult compilationResult) {
-            return new CompilationResultBuilder(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, compilationResult);
+                        FrameContext frameContext, OptionValues options, DebugContext debug, CompilationResult compilationResult) {
+            return new CompilationResultBuilder(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, debug, compilationResult);
         }
     };
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java	Fri Jul 07 09:40:47 2017 -0700
@@ -35,9 +35,8 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
 import org.graalvm.compiler.lir.LIR;
@@ -82,12 +81,12 @@
         new Optimization(lirGenRes.getLIR(), lirGen).apply();
     }
 
-    private static final DebugCounter constantsTotal = Debug.counter("ConstantLoadOptimization[total]");
-    private static final DebugCounter phiConstantsSkipped = Debug.counter("ConstantLoadOptimization[PhisSkipped]");
-    private static final DebugCounter singleUsageConstantsSkipped = Debug.counter("ConstantLoadOptimization[SingleUsageSkipped]");
-    private static final DebugCounter usageAtDefinitionSkipped = Debug.counter("ConstantLoadOptimization[UsageAtDefinitionSkipped]");
-    private static final DebugCounter materializeAtDefinitionSkipped = Debug.counter("ConstantLoadOptimization[MaterializeAtDefinitionSkipped]");
-    private static final DebugCounter constantsOptimized = Debug.counter("ConstantLoadOptimization[optimized]");
+    private static final CounterKey constantsTotal = DebugContext.counter("ConstantLoadOptimization[total]");
+    private static final CounterKey phiConstantsSkipped = DebugContext.counter("ConstantLoadOptimization[PhisSkipped]");
+    private static final CounterKey singleUsageConstantsSkipped = DebugContext.counter("ConstantLoadOptimization[SingleUsageSkipped]");
+    private static final CounterKey usageAtDefinitionSkipped = DebugContext.counter("ConstantLoadOptimization[UsageAtDefinitionSkipped]");
+    private static final CounterKey materializeAtDefinitionSkipped = DebugContext.counter("ConstantLoadOptimization[MaterializeAtDefinitionSkipped]");
+    private static final CounterKey constantsOptimized = DebugContext.counter("ConstantLoadOptimization[optimized]");
 
     private static final class Optimization {
         private final LIR lir;
@@ -97,9 +96,11 @@
         private final BitSet defined;
         private final BlockMap<List<UseEntry>> blockMap;
         private final BlockMap<LIRInsertionBuffer> insertionBuffers;
+        private final DebugContext debug;
 
         private Optimization(LIR lir, LIRGeneratorTool lirGen) {
             this.lir = lir;
+            this.debug = lir.getDebug();
             this.lirGen = lirGen;
             this.map = new VariableMap<>();
             this.phiConstants = new BitSet();
@@ -110,8 +111,8 @@
 
         @SuppressWarnings("try")
         private void apply() {
-            try (Indent indent = Debug.logAndIndent("ConstantLoadOptimization")) {
-                try (Scope s = Debug.scope("BuildDefUseTree")) {
+            try (Indent indent = debug.logAndIndent("ConstantLoadOptimization")) {
+                try (DebugContext.Scope s = debug.scope("BuildDefUseTree")) {
                     // build DefUseTree
                     for (AbstractBlockBase<?> b : lir.getControlFlowGraph().getBlocks()) {
                         this.analyzeBlock(b);
@@ -121,17 +122,17 @@
                         if (t.usageCount() > 1) {
                             return true;
                         } else {
-                            singleUsageConstantsSkipped.increment();
+                            singleUsageConstantsSkipped.increment(debug);
                             return false;
                         }
                     });
                     // collect block map
                     map.forEach(tree -> tree.forEach(this::addUsageToBlockMap));
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
 
-                try (Scope s = Debug.scope("BuildConstantTree")) {
+                try (DebugContext.Scope s = debug.scope("BuildConstantTree")) {
                     // create ConstantTree
                     map.forEach(this::createConstantTree);
 
@@ -142,7 +143,7 @@
 
                     assert verifyStates();
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
             }
         }
@@ -191,7 +192,7 @@
          */
         @SuppressWarnings("try")
         private void analyzeBlock(AbstractBlockBase<?> block) {
-            try (Indent indent = Debug.logAndIndent("Block: %s", block)) {
+            try (Indent indent = debug.logAndIndent("Block: %s", block)) {
 
                 InstructionValueConsumer loadConsumer = (instruction, value, mode, flags) -> {
                     if (isVariable(value)) {
@@ -201,19 +202,19 @@
                             if (!defined.get(var.index)) {
                                 defined.set(var.index);
                                 if (isConstantLoad(instruction)) {
-                                    Debug.log("constant load: %s", instruction);
+                                    debug.log("constant load: %s", instruction);
                                     map.put(var, new DefUseTree(instruction, block));
-                                    constantsTotal.increment();
+                                    constantsTotal.increment(debug);
                                 }
                             } else {
                                 // Variable is redefined, this only happens for constant loads
                                 // introduced by phi resolution -> ignore.
                                 DefUseTree removed = map.remove(var);
                                 if (removed != null) {
-                                    phiConstantsSkipped.increment();
+                                    phiConstantsSkipped.increment(debug);
                                 }
                                 phiConstants.set(var.index);
-                                Debug.log(Debug.VERBOSE_LEVEL, "Removing phi variable: %s", var);
+                                debug.log(DebugContext.VERBOSE_LEVEL, "Removing phi variable: %s", var);
                             }
                         } else {
                             assert defined.get(var.index) : "phi but not defined? " + var;
@@ -228,7 +229,7 @@
                             DefUseTree tree = map.get(var);
                             if (tree != null) {
                                 tree.addUsage(block, instruction, value);
-                                Debug.log("usage of %s : %s", var, instruction);
+                                debug.log("usage of %s : %s", var, instruction);
                             }
                         }
                     }
@@ -257,40 +258,40 @@
 
             if (constTree.get(Flags.USAGE, tree.getBlock())) {
                 // usage in the definition block -> no optimization
-                usageAtDefinitionSkipped.increment();
+                usageAtDefinitionSkipped.increment(debug);
                 return;
             }
 
             constTree.markBlocks();
 
-            NodeCost cost = ConstantTreeAnalyzer.analyze(constTree, tree.getBlock());
+            NodeCost cost = ConstantTreeAnalyzer.analyze(debug, constTree, tree.getBlock());
             int usageCount = cost.getUsages().size();
             assert usageCount == tree.usageCount() : "Usage count differs: " + usageCount + " vs. " + tree.usageCount();
 
-            if (Debug.isLogEnabled()) {
-                try (Indent i = Debug.logAndIndent("Variable: %s, Block: %s, prob.: %f", tree.getVariable(), tree.getBlock(), tree.getBlock().probability())) {
-                    Debug.log("Usages result: %s", cost);
+            if (debug.isLogEnabled()) {
+                try (Indent i = debug.logAndIndent("Variable: %s, Block: %s, prob.: %f", tree.getVariable(), tree.getBlock(), tree.getBlock().probability())) {
+                    debug.log("Usages result: %s", cost);
                 }
 
             }
 
             if (cost.getNumMaterializations() > 1 || cost.getBestCost() < tree.getBlock().probability()) {
-                try (Scope s = Debug.scope("CLOmodify", constTree); Indent i = Debug.logAndIndent("Replacing %s = %s", tree.getVariable(), tree.getConstant().toValueString())) {
+                try (DebugContext.Scope s = debug.scope("CLOmodify", constTree); Indent i = debug.logAndIndent("Replacing %s = %s", tree.getVariable(), tree.getConstant().toValueString())) {
                     // mark original load for removal
                     deleteInstruction(tree);
-                    constantsOptimized.increment();
+                    constantsOptimized.increment(debug);
 
                     // collect result
                     createLoads(tree, constTree, tree.getBlock());
 
                 } catch (Throwable e) {
-                    throw Debug.handle(e);
+                    throw debug.handle(e);
                 }
             } else {
                 // no better solution found
-                materializeAtDefinitionSkipped.increment();
+                materializeAtDefinitionSkipped.increment(debug);
             }
-            Debug.dump(Debug.DETAILED_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
+            debug.dump(DebugContext.DETAILED_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
         }
 
         private void createLoads(DefUseTree tree, ConstantTree constTree, AbstractBlockBase<?> startBlock) {
@@ -322,11 +323,11 @@
             LIRInstruction move = lirGen.getSpillMoveFactory().createLoad(variable, constant);
             // insert instruction
             getInsertionBuffer(block).append(1, move);
-            Debug.log("new move (%s) and inserted in block %s", move, block);
+            debug.log("new move (%s) and inserted in block %s", move, block);
             // update usages
             for (UseEntry u : usages) {
                 u.setValue(variable);
-                Debug.log("patched instruction %s", u.getInstruction());
+                debug.log("patched instruction %s", u.getInstruction());
             }
         }
 
@@ -361,7 +362,7 @@
         private void deleteInstruction(DefUseTree tree) {
             AbstractBlockBase<?> block = tree.getBlock();
             LIRInstruction instruction = tree.getInstruction();
-            Debug.log("deleting instruction %s from block %s", instruction, block);
+            debug.log("deleting instruction %s from block %s", instruction, block);
             lir.getLIRforBlock(block).set(instruction.id(), null);
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTreeAnalyzer.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTreeAnalyzer.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,8 +29,7 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.constopt.ConstantTree.Flags;
 import org.graalvm.compiler.lir.constopt.ConstantTree.NodeCost;
@@ -43,13 +42,13 @@
     private final BitSet visited;
 
     @SuppressWarnings("try")
-    public static NodeCost analyze(ConstantTree tree, AbstractBlockBase<?> startBlock) {
-        try (Scope s = Debug.scope("ConstantTreeAnalyzer")) {
+    public static NodeCost analyze(DebugContext debug, ConstantTree tree, AbstractBlockBase<?> startBlock) {
+        try (DebugContext.Scope s = debug.scope("ConstantTreeAnalyzer")) {
             ConstantTreeAnalyzer analyzer = new ConstantTreeAnalyzer(tree);
-            analyzer.analyzeBlocks(startBlock);
+            analyzer.analyzeBlocks(debug, startBlock);
             return tree.getCost(startBlock);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -67,33 +66,33 @@
      * @param startBlock The start block of the dominator subtree.
      */
     @SuppressWarnings("try")
-    private void analyzeBlocks(AbstractBlockBase<?> startBlock) {
+    private void analyzeBlocks(DebugContext debug, AbstractBlockBase<?> startBlock) {
         Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
         worklist.offerLast(startBlock);
         while (!worklist.isEmpty()) {
             AbstractBlockBase<?> block = worklist.pollLast();
-            try (Indent i = Debug.logAndIndent(Debug.VERBOSE_LEVEL, "analyze: %s", block)) {
+            try (Indent i = debug.logAndIndent(DebugContext.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_LEVEL, "leaf block");
+                    debug.log(DebugContext.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_LEVEL, "not marked");
+                    debug.log(DebugContext.VERBOSE_LEVEL, "not marked");
                     worklist.offerLast(block);
                     AbstractBlockBase<?> dominated = block.getFirstDominated();
                     while (dominated != null) {
-                        filteredPush(worklist, dominated);
+                        filteredPush(debug, worklist, dominated);
                         dominated = dominated.getDominatedSibling();
                     }
                     visited.set(block.getId());
                 } else {
-                    Debug.log(Debug.VERBOSE_LEVEL, "marked");
+                    debug.log(DebugContext.VERBOSE_LEVEL, "marked");
                     // otherwise, process block
                     process(block);
                 }
@@ -160,9 +159,9 @@
         return probabilityBlock * Math.pow(0.9, numMat - 1) < probabilityChildren;
     }
 
-    private void filteredPush(Deque<AbstractBlockBase<?>> worklist, AbstractBlockBase<?> block) {
+    private void filteredPush(DebugContext debug, Deque<AbstractBlockBase<?>> worklist, AbstractBlockBase<?> block) {
         if (isMarked(block)) {
-            Debug.log(Debug.VERBOSE_LEVEL, "adding %s to the worklist", block);
+            debug.log(DebugContext.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/debug/LIRGenerationDebugContext.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/debug/LIRGenerationDebugContext.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.lir.debug;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 
 import jdk.vm.ci.meta.Value;
@@ -38,17 +38,17 @@
      */
     Object getSourceForOperand(Value value);
 
-    static LIRGenerationDebugContext getFromDebugContext() {
-        if (Debug.isEnabled()) {
-            LIRGenerationDebugContext lirGen = Debug.contextLookup(LIRGenerationDebugContext.class);
+    static LIRGenerationDebugContext getFromDebugContext(DebugContext debug) {
+        if (debug.areScopesEnabled()) {
+            LIRGenerationDebugContext lirGen = debug.contextLookup(LIRGenerationDebugContext.class);
             assert lirGen != null;
             return lirGen;
         }
         return null;
     }
 
-    static Object getSourceForOperandFromDebugContext(Value value) {
-        LIRGenerationDebugContext gen = getFromDebugContext();
+    static Object getSourceForOperandFromDebugContext(DebugContext debug, Value value) {
+        LIRGenerationDebugContext gen = getFromDebugContext(debug);
         if (gen != null) {
             return gen.getSourceForOperand(value);
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/dfa/LocationMarker.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/dfa/LocationMarker.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,7 +30,7 @@
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionStateProcedure;
 import org.graalvm.compiler.lir.LIR;
@@ -102,7 +102,8 @@
     @SuppressWarnings("try")
     private void processBlock(AbstractBlockBase<?> block, UniqueWorkList worklist) {
         if (updateOutBlock(block)) {
-            try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
+            DebugContext debug = lir.getDebug();
+            try (Indent indent = debug.logAndIndent("handle block %s", block)) {
                 currentSet = liveOutMap.get(block).copy();
                 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
                 for (int i = instructions.size() - 1; i >= 0; i--) {
@@ -128,7 +129,8 @@
      */
     @SuppressWarnings("try")
     private void processInstructionBottomUp(LIRInstruction op) {
-        try (Indent indent = Debug.logAndIndent("handle op %d, %s", op.id(), op)) {
+        DebugContext debug = lir.getDebug();
+        try (Indent indent = debug.logAndIndent("handle op %d, %s", op.id(), op)) {
             // kills
 
             op.visitEachTemp(defConsumer);
@@ -162,8 +164,9 @@
         public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
             if (shouldProcessValue(operand)) {
                 // no need to insert values and derived reference
-                if (Debug.isLogEnabled()) {
-                    Debug.log("set operand: %s", operand);
+                DebugContext debug = lir.getDebug();
+                if (debug.isLogEnabled()) {
+                    debug.log("set operand: %s", operand);
                 }
                 currentSet.put(operand);
             }
@@ -174,8 +177,9 @@
         @Override
         public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
             if (shouldProcessValue(operand)) {
-                if (Debug.isLogEnabled()) {
-                    Debug.log("clear operand: %s", operand);
+                DebugContext debug = lir.getDebug();
+                if (debug.isLogEnabled()) {
+                    debug.log("clear operand: %s", operand);
                 }
                 currentSet.remove(operand);
             } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/framemap/FrameMapBuilderImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/framemap/FrameMapBuilderImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,7 +31,7 @@
 
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
 import org.graalvm.compiler.lir.LIR;
@@ -118,7 +118,8 @@
     @Override
     @SuppressWarnings("try")
     public FrameMap buildFrameMap(LIRGenerationResult res) {
-        if (Debug.isEnabled()) {
+        DebugContext debug = res.getLIR().getDebug();
+        if (debug.areScopesEnabled()) {
             verifyStackSlotAllocation(res);
         }
         for (CallingConvention cc : calls) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,7 +24,7 @@
 
 import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.common.CompilationIdentifier.Verbosity;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.framemap.FrameMap;
@@ -65,7 +65,8 @@
      * Adds a comment to a {@link LIRInstruction}. Existing comments are replaced.
      */
     public final void setComment(LIRInstruction op, String comment) {
-        if (Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
+        DebugContext debug = lir.getDebug();
+        if (debug.isDumpEnabled(DebugContext.BASIC_LEVEL)) {
             if (comments == null) {
                 comments = EconomicMap.create(Equivalence.IDENTITY);
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -47,6 +47,7 @@
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.lir.ConstantValue;
+import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRFrameState;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRVerifier;
@@ -302,12 +303,13 @@
 
     @Override
     public <I extends LIRInstruction> I append(I op) {
+        LIR lir = res.getLIR();
         if (printIrWithLir) {
             TTY.println(op.toStringWithIdPrefix());
             TTY.println();
         }
         assert LIRVerifier.verify(op);
-        ArrayList<LIRInstruction> lirForBlock = res.getLIR().getLIRforBlock(getCurrentBlock());
+        ArrayList<LIRInstruction> lirForBlock = lir.getLIRforBlock(getCurrentBlock());
         op.setPosition(currentPosition);
         lirForBlock.add(op);
         return op;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/EconomyAllocationStage.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/EconomyAllocationStage.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,14 +22,26 @@
  */
 package org.graalvm.compiler.lir.phases;
 
+import static org.graalvm.compiler.core.common.GraalOptions.TraceRA;
+
 import org.graalvm.compiler.lir.alloc.lsra.LinearScanPhase;
+import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessAnalysisPhase;
+import org.graalvm.compiler.lir.alloc.trace.TraceBuilderPhase;
+import org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase;
 import org.graalvm.compiler.lir.dfa.LocationMarkerPhase;
 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
 import org.graalvm.compiler.lir.stackslotalloc.SimpleStackSlotAllocator;
+import org.graalvm.compiler.options.OptionValues;
 
 public class EconomyAllocationStage extends LIRPhaseSuite<AllocationContext> {
-    public EconomyAllocationStage() {
-        appendPhase(new LinearScanPhase());
+    public EconomyAllocationStage(OptionValues options) {
+        if (TraceRA.getValue(options)) {
+            appendPhase(new TraceBuilderPhase());
+            appendPhase(new GlobalLivenessAnalysisPhase());
+            appendPhase(new TraceRegisterAllocationPhase());
+        } else {
+            appendPhase(new LinearScanPhase());
+        }
 
         // build frame map
         appendPhase(new SimpleStackSlotAllocator());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,11 +24,10 @@
 
 import java.util.regex.Pattern;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.MemUseTrackerKey;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 import org.graalvm.compiler.options.Option;
@@ -53,27 +52,27 @@
     /**
      * Records time spent within {@link #apply}.
      */
-    private final DebugTimer timer;
+    private final TimerKey timer;
 
     /**
      * Records memory usage within {@link #apply}.
      */
-    private final DebugMemUseTracker memUseTracker;
+    private final MemUseTrackerKey memUseTracker;
 
     public static final class LIRPhaseStatistics {
         /**
          * Records time spent within {@link #apply}.
          */
-        public final DebugTimer timer;
+        public final TimerKey timer;
 
         /**
          * Records memory usage within {@link #apply}.
          */
-        public final DebugMemUseTracker memUseTracker;
+        public final MemUseTrackerKey memUseTracker;
 
         public LIRPhaseStatistics(Class<?> clazz) {
-            timer = Debug.timer("LIRPhaseTime_%s", clazz);
-            memUseTracker = Debug.memUseTracker("LIRPhaseMemUse_%s", clazz);
+            timer = DebugContext.timer("LIRPhaseTime_%s", clazz);
+            memUseTracker = DebugContext.memUseTracker("LIRPhaseMemUse_%s", clazz);
         }
     }
 
@@ -110,23 +109,25 @@
 
     @SuppressWarnings("try")
     public final void apply(TargetDescription target, LIRGenerationResult lirGenRes, C context, boolean dumpLIR) {
-        try (Scope s = Debug.scope(getName(), this)) {
-            try (DebugCloseable a = timer.start(); DebugCloseable c = memUseTracker.start()) {
+        DebugContext debug = lirGenRes.getLIR().getDebug();
+        try (DebugContext.Scope s = debug.scope(getName(), this)) {
+            try (DebugCloseable a = timer.start(debug); DebugCloseable c = memUseTracker.start(debug)) {
                 run(target, lirGenRes, context);
-                if (dumpLIR && Debug.isEnabled()) {
+                if (dumpLIR && debug.areScopesEnabled()) {
                     dumpAfter(lirGenRes);
                 }
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
     private void dumpAfter(LIRGenerationResult lirGenRes) {
         boolean isStage = this instanceof LIRPhaseSuite;
         if (!isStage) {
-            if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
-                Debug.dump(Debug.INFO_LEVEL, lirGenRes.getLIR(), "After %s", getName());
+            DebugContext debug = lirGenRes.getLIR().getDebug();
+            if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
+                debug.dump(DebugContext.INFO_LEVEL, lirGenRes.getLIR(), "After %s", getName());
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ssa/SSAVerifier.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/ssa/SSAVerifier.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,17 +23,16 @@
 
 package org.graalvm.compiler.lir.ssa;
 
+import static jdk.vm.ci.code.ValueUtil.isRegister;
 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
-import static jdk.vm.ci.code.ValueUtil.isRegister;
 
 import java.util.BitSet;
 import java.util.EnumSet;
 import java.util.HashMap;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
 import org.graalvm.compiler.lir.LIR;
@@ -67,12 +66,13 @@
 
     @SuppressWarnings("try")
     public boolean verify() {
-        try (Scope s = Debug.scope("SSAVerifier", lir)) {
+        DebugContext debug = lir.getDebug();
+        try (DebugContext.Scope s = debug.scope("SSAVerifier", lir)) {
             for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
                 doBlock(block);
             }
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
         return true;
     }
@@ -87,7 +87,7 @@
                 doBlock(pred);
             }
         }
-        try (Indent indent = Debug.logAndIndent(Debug.INFO_LEVEL, "handle block %s", b)) {
+        try (Indent indent = lir.getDebug().logAndIndent(DebugContext.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/FixPointIntervalBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/FixPointIntervalBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,8 +33,8 @@
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueConsumer;
 import org.graalvm.compiler.lir.InstructionValueProcedure;
@@ -42,9 +42,9 @@
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
-import org.graalvm.util.Equivalence;
+import org.graalvm.compiler.lir.VirtualStackSlot;
 import org.graalvm.util.EconomicSet;
-import org.graalvm.compiler.lir.VirtualStackSlot;
+import org.graalvm.util.Equivalence;
 
 import jdk.vm.ci.meta.Value;
 
@@ -62,7 +62,7 @@
     /**
      * The number of allocated stack slots.
      */
-    private static final DebugCounter uninitializedSlots = Debug.counter("StackSlotAllocator[uninitializedSlots]");
+    private static final CounterKey uninitializedSlots = DebugContext.counter("StackSlotAllocator[uninitializedSlots]");
 
     FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId) {
         this.lir = lir;
@@ -113,8 +113,9 @@
 
     @SuppressWarnings("try")
     private void processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist) {
+        DebugContext debug = lir.getDebug();
         if (updateOutBlock(block)) {
-            try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
+            try (Indent indent = debug.logAndIndent("handle block %s", block)) {
                 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
                 // get out set and mark intervals
                 BitSet outSet = liveOutMap.get(block);
@@ -143,9 +144,10 @@
 
     @SuppressWarnings("try")
     private void printLiveSet(String label, BitSet liveSet) {
-        if (Debug.isLogEnabled()) {
-            try (Indent indent = Debug.logAndIndent(label)) {
-                Debug.log("%s", liveSetToString(liveSet));
+        DebugContext debug = lir.getDebug();
+        if (debug.isLogEnabled()) {
+            try (Indent indent = debug.logAndIndent(label)) {
+                debug.log("%s", liveSetToString(liveSet));
             }
         }
     }
@@ -160,17 +162,19 @@
     }
 
     private void markOutInterval(BitSet outSet, int blockEndOpId) {
+        DebugContext debug = lir.getDebug();
         for (int i = outSet.nextSetBit(0); i >= 0; i = outSet.nextSetBit(i + 1)) {
             StackInterval interval = getIntervalFromStackId(i);
-            Debug.log("mark live operand: %s", interval.getOperand());
+            debug.log("mark live operand: %s", interval.getOperand());
             interval.addTo(blockEndOpId);
         }
     }
 
     private void markInInterval(BitSet inSet, int blockFirstOpId) {
+        DebugContext debug = lir.getDebug();
         for (int i = inSet.nextSetBit(0); i >= 0; i = inSet.nextSetBit(i + 1)) {
             StackInterval interval = getIntervalFromStackId(i);
-            Debug.log("mark live operand: %s", interval.getOperand());
+            debug.log("mark live operand: %s", interval.getOperand());
             interval.addFrom(blockFirstOpId);
         }
     }
@@ -192,7 +196,8 @@
          */
         @SuppressWarnings("try")
         private void processInstructionBottomUp(LIRInstruction op) {
-            try (Indent indent = Debug.logAndIndent("handle op %d, %s", op.id(), op)) {
+            DebugContext debug = lir.getDebug();
+            try (Indent indent = debug.logAndIndent("handle op %d, %s", op.id(), op)) {
                 // kills
                 op.visitEachTemp(defConsumer);
                 op.visitEachOutput(defConsumer);
@@ -210,11 +215,12 @@
             @Override
             public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
                 if (isVirtualStackSlot(operand)) {
+                    DebugContext debug = lir.getDebug();
                     VirtualStackSlot vslot = asVirtualStackSlot(operand);
                     addUse(vslot, inst, flags);
                     addRegisterHint(inst, vslot, mode, flags, false);
                     usePos.add(inst);
-                    Debug.log("set operand: %s", operand);
+                    debug.log("set operand: %s", operand);
                     currentSet.set(vslot.getId());
                 }
             }
@@ -224,11 +230,12 @@
             @Override
             public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
                 if (isVirtualStackSlot(operand)) {
+                    DebugContext debug = lir.getDebug();
                     VirtualStackSlot vslot = asVirtualStackSlot(operand);
                     addDef(vslot, inst);
                     addRegisterHint(inst, vslot, mode, flags, true);
                     usePos.add(inst);
-                    Debug.log("clear operand: %s", operand);
+                    debug.log("clear operand: %s", operand);
                     currentSet.clear(vslot.getId());
                 }
 
@@ -240,8 +247,9 @@
             if (flags.contains(OperandFlag.UNINITIALIZED)) {
                 // Stack slot is marked uninitialized so we have to assume it is live all
                 // the time.
-                if (Debug.isCountEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) {
-                    uninitializedSlots.increment();
+                DebugContext debug = lir.getDebug();
+                if (debug.isCountEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) {
+                    uninitializedSlots.increment(debug);
                 }
                 interval.addFrom(0);
                 interval.addTo(maxOpId);
@@ -270,8 +278,9 @@
                             } else {
                                 from.setLocationHint(to);
                             }
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("operation %s at opId %d: added hint from interval %s to %s", op, op.id(), from, to);
+                            DebugContext debug = lir.getDebug();
+                            if (debug.isLogEnabled()) {
+                                debug.log("operation %s at opId %d: added hint from interval %s to %s", op, op.id(), from, to);
                             }
 
                             return registerHint;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/LSStackSlotAllocator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/LSStackSlotAllocator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.lir.stackslotalloc;
 
+import static org.graalvm.compiler.debug.DebugContext.BASIC_LEVEL;
 import static org.graalvm.compiler.lir.LIRValueUtil.asVirtualStackSlot;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
 import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
@@ -35,11 +36,10 @@
 import java.util.PriorityQueue;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
@@ -80,12 +80,12 @@
         // @formatter:on
     }
 
-    private static final DebugTimer MainTimer = Debug.timer("LSStackSlotAllocator");
-    private static final DebugTimer NumInstTimer = Debug.timer("LSStackSlotAllocator[NumberInstruction]");
-    private static final DebugTimer BuildIntervalsTimer = Debug.timer("LSStackSlotAllocator[BuildIntervals]");
-    private static final DebugTimer VerifyIntervalsTimer = Debug.timer("LSStackSlotAllocator[VerifyIntervals]");
-    private static final DebugTimer AllocateSlotsTimer = Debug.timer("LSStackSlotAllocator[AllocateSlots]");
-    private static final DebugTimer AssignSlotsTimer = Debug.timer("LSStackSlotAllocator[AssignSlots]");
+    private static final TimerKey MainTimer = DebugContext.timer("LSStackSlotAllocator");
+    private static final TimerKey NumInstTimer = DebugContext.timer("LSStackSlotAllocator[NumberInstruction]");
+    private static final TimerKey BuildIntervalsTimer = DebugContext.timer("LSStackSlotAllocator[BuildIntervals]");
+    private static final TimerKey VerifyIntervalsTimer = DebugContext.timer("LSStackSlotAllocator[VerifyIntervals]");
+    private static final TimerKey AllocateSlotsTimer = DebugContext.timer("LSStackSlotAllocator[AllocateSlots]");
+    private static final TimerKey AssignSlotsTimer = DebugContext.timer("LSStackSlotAllocator[AssignSlots]");
 
     @Override
     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) {
@@ -96,7 +96,7 @@
     @SuppressWarnings("try")
     public static void allocateStackSlots(FrameMapBuilderTool builder, LIRGenerationResult res) {
         if (builder.getNumberOfStackSlots() > 0) {
-            try (DebugCloseable t = MainTimer.start()) {
+            try (DebugCloseable t = MainTimer.start(res.getLIR().getDebug())) {
                 new Allocator(res.getLIR(), builder).allocate();
             }
         }
@@ -105,6 +105,7 @@
     private static final class Allocator {
 
         private final LIR lir;
+        private final DebugContext debug;
         private final FrameMapBuilderTool frameMapBuilder;
         private final StackInterval[] stackSlotMap;
         private final PriorityQueue<StackInterval> unhandled;
@@ -115,6 +116,7 @@
         @SuppressWarnings("try")
         private Allocator(LIR lir, FrameMapBuilderTool frameMapBuilder) {
             this.lir = lir;
+            this.debug = lir.getDebug();
             this.frameMapBuilder = frameMapBuilder;
             this.stackSlotMap = new StackInterval[frameMapBuilder.getNumberOfStackSlots()];
             this.sortedBlocks = lir.getControlFlowGraph().getBlocks();
@@ -124,7 +126,7 @@
             // insert by to
             this.active = new PriorityQueue<>((a, b) -> a.to() - b.to());
 
-            try (DebugCloseable t = NumInstTimer.start()) {
+            try (DebugCloseable t = NumInstTimer.start(debug)) {
                 // step 1: number instructions
                 this.maxOpId = numberInstructions(lir, sortedBlocks);
             }
@@ -132,37 +134,38 @@
 
         @SuppressWarnings("try")
         private void allocate() {
-            Debug.dump(Debug.VERBOSE_LEVEL, lir, "After StackSlot numbering");
+            debug.dump(DebugContext.VERBOSE_LEVEL, lir, "After StackSlot numbering");
 
-            long currentFrameSize = StackSlotAllocatorUtil.allocatedFramesize.isEnabled() ? frameMapBuilder.getFrameMap().currentFrameSize() : 0;
+            boolean allocationFramesizeEnabled = StackSlotAllocatorUtil.allocatedFramesize.isEnabled(debug);
+            long currentFrameSize = allocationFramesizeEnabled ? frameMapBuilder.getFrameMap().currentFrameSize() : 0;
             EconomicSet<LIRInstruction> usePos;
             // step 2: build intervals
-            try (Scope s = Debug.scope("StackSlotAllocationBuildIntervals"); Indent indent = Debug.logAndIndent("BuildIntervals"); DebugCloseable t = BuildIntervalsTimer.start()) {
+            try (DebugContext.Scope s = debug.scope("StackSlotAllocationBuildIntervals"); Indent indent = debug.logAndIndent("BuildIntervals"); DebugCloseable t = BuildIntervalsTimer.start(debug)) {
                 usePos = buildIntervals();
             }
             // step 3: verify intervals
-            if (Debug.isEnabled()) {
-                try (DebugCloseable t = VerifyIntervalsTimer.start()) {
+            if (debug.areScopesEnabled()) {
+                try (DebugCloseable t = VerifyIntervalsTimer.start(debug)) {
                     assert verifyIntervals();
                 }
             }
-            if (Debug.isDumpEnabled(Debug.VERBOSE_LEVEL)) {
+            if (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) {
                 dumpIntervals("Before stack slot allocation");
             }
             // step 4: allocate stack slots
-            try (DebugCloseable t = AllocateSlotsTimer.start()) {
+            try (DebugCloseable t = AllocateSlotsTimer.start(debug)) {
                 allocateStackSlots();
             }
-            if (Debug.isDumpEnabled(Debug.VERBOSE_LEVEL)) {
+            if (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) {
                 dumpIntervals("After stack slot allocation");
             }
 
             // step 5: assign stack slots
-            try (DebugCloseable t = AssignSlotsTimer.start()) {
+            try (DebugCloseable t = AssignSlotsTimer.start(debug)) {
                 assignStackSlots(usePos);
             }
-            if (StackSlotAllocatorUtil.allocatedFramesize.isEnabled()) {
-                StackSlotAllocatorUtil.allocatedFramesize.add(frameMapBuilder.getFrameMap().currentFrameSize() - currentFrameSize);
+            if (allocationFramesizeEnabled) {
+                StackSlotAllocatorUtil.allocatedFramesize.add(debug, frameMapBuilder.getFrameMap().currentFrameSize() - currentFrameSize);
             }
         }
 
@@ -230,7 +233,7 @@
             }
 
             for (StackInterval current = activateNext(); current != null; current = activateNext()) {
-                try (Indent indent = Debug.logAndIndent("allocate %s", current)) {
+                try (Indent indent = debug.logAndIndent("allocate %s", current)) {
                     allocateSlot(current);
                 }
             }
@@ -244,8 +247,8 @@
                 // No reuse of ranges (yet).
                 VirtualStackSlotRange slotRange = (VirtualStackSlotRange) virtualSlot;
                 location = frameMapBuilder.getFrameMap().allocateStackSlots(slotRange.getSlots(), slotRange.getObjects());
-                StackSlotAllocatorUtil.virtualFramesize.add(frameMapBuilder.getFrameMap().spillSlotRangeSize(slotRange.getSlots()));
-                StackSlotAllocatorUtil.allocatedSlots.increment();
+                StackSlotAllocatorUtil.virtualFramesize.add(debug, frameMapBuilder.getFrameMap().spillSlotRangeSize(slotRange.getSlots()));
+                StackSlotAllocatorUtil.allocatedSlots.increment(debug);
             } else {
                 assert virtualSlot instanceof SimpleVirtualStackSlot : "Unexpected VirtualStackSlot type: " + virtualSlot;
                 StackSlot slot = findFreeSlot((SimpleVirtualStackSlot) virtualSlot);
@@ -255,17 +258,17 @@
                      * might not match.
                      */
                     location = StackSlot.get(current.kind(), slot.getRawOffset(), slot.getRawAddFrameSize());
-                    StackSlotAllocatorUtil.reusedSlots.increment();
-                    Debug.log(Debug.BASIC_LEVEL, "Reuse stack slot %s (reallocated from %s) for virtual stack slot %s", location, slot, virtualSlot);
+                    StackSlotAllocatorUtil.reusedSlots.increment(debug);
+                    debug.log(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_LEVEL, "New stack slot %s for virtual stack slot %s", location, virtualSlot);
+                    StackSlotAllocatorUtil.virtualFramesize.add(debug, frameMapBuilder.getFrameMap().spillSlotSize(virtualSlot.getValueKind()));
+                    StackSlotAllocatorUtil.allocatedSlots.increment(debug);
+                    debug.log(BASIC_LEVEL, "New stack slot %s for virtual stack slot %s", location, virtualSlot);
                 }
             }
-            Debug.log("Allocate location %s for interval %s", location, current);
+            debug.log("Allocate location %s for interval %s", location, current);
             current.setLocation(location);
         }
 
@@ -364,7 +367,7 @@
             for (int id = next.from(); activePeekId() < id;) {
                 finished(active.poll());
             }
-            Debug.log("active %s", next);
+            debug.log("active %s", next);
             active.add(next);
             return next;
         }
@@ -386,7 +389,7 @@
          */
         private void finished(StackInterval interval) {
             StackSlot location = interval.location();
-            Debug.log("finished %s (freeing %s)", interval, location);
+            debug.log("finished %s (freeing %s)", interval, location);
             freeSlot(location);
         }
 
@@ -434,7 +437,7 @@
         }
 
         private void dumpIntervals(String label) {
-            Debug.dump(Debug.VERBOSE_LEVEL, new StackIntervalDumper(Arrays.copyOf(stackSlotMap, stackSlotMap.length)), label);
+            debug.dump(DebugContext.VERBOSE_LEVEL, new StackIntervalDumper(Arrays.copyOf(stackSlotMap, stackSlotMap.length)), label);
         }
 
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/SimpleStackSlotAllocator.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/SimpleStackSlotAllocator.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,10 +29,9 @@
 import static org.graalvm.compiler.lir.stackslotalloc.StackSlotAllocatorUtil.virtualFramesize;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
-import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.ValueProcedure;
 import org.graalvm.compiler.lir.VirtualStackSlot;
@@ -54,44 +53,47 @@
     }
 
     public void allocateStackSlots(FrameMapBuilderTool builder, LIRGenerationResult res) {
+        DebugContext debug = res.getLIR().getDebug();
         StackSlot[] mapping = new StackSlot[builder.getNumberOfStackSlots()];
-        long currentFrameSize = allocatedFramesize.isEnabled() ? builder.getFrameMap().currentFrameSize() : 0;
+        boolean allocatedFramesizeEnabled = allocatedFramesize.isEnabled(debug);
+        long currentFrameSize = allocatedFramesizeEnabled ? builder.getFrameMap().currentFrameSize() : 0;
         for (VirtualStackSlot virtualSlot : builder.getStackSlots()) {
             final StackSlot slot;
             if (virtualSlot instanceof SimpleVirtualStackSlot) {
                 slot = mapSimpleVirtualStackSlot(builder, (SimpleVirtualStackSlot) virtualSlot);
-                virtualFramesize.add(builder.getFrameMap().spillSlotSize(virtualSlot.getValueKind()));
+                virtualFramesize.add(debug, builder.getFrameMap().spillSlotSize(virtualSlot.getValueKind()));
             } else if (virtualSlot instanceof VirtualStackSlotRange) {
                 VirtualStackSlotRange slotRange = (VirtualStackSlotRange) virtualSlot;
                 slot = mapVirtualStackSlotRange(builder, slotRange);
-                virtualFramesize.add(builder.getFrameMap().spillSlotRangeSize(slotRange.getSlots()));
+                virtualFramesize.add(debug, builder.getFrameMap().spillSlotRangeSize(slotRange.getSlots()));
             } else {
                 throw GraalError.shouldNotReachHere("Unknown VirtualStackSlot: " + virtualSlot);
             }
-            allocatedSlots.increment();
+            allocatedSlots.increment(debug);
             mapping[virtualSlot.getId()] = slot;
         }
         updateLIR(res, mapping);
-        if (allocatedFramesize.isEnabled()) {
-            allocatedFramesize.add(builder.getFrameMap().currentFrameSize() - currentFrameSize);
+        if (allocatedFramesizeEnabled) {
+            allocatedFramesize.add(debug, builder.getFrameMap().currentFrameSize() - currentFrameSize);
         }
     }
 
     @SuppressWarnings("try")
     protected void updateLIR(LIRGenerationResult res, StackSlot[] mapping) {
-        try (Scope scope = Debug.scope("StackSlotMappingLIR")) {
+        DebugContext debug = res.getLIR().getDebug();
+        try (DebugContext.Scope scope = debug.scope("StackSlotMappingLIR")) {
             ValueProcedure updateProc = (value, mode, flags) -> {
                 if (isVirtualStackSlot(value)) {
                     StackSlot stackSlot = mapping[asVirtualStackSlot(value).getId()];
-                    Debug.log("map %s -> %s", value, stackSlot);
+                    debug.log("map %s -> %s", value, stackSlot);
                     return stackSlot;
                 }
                 return value;
             };
             for (AbstractBlockBase<?> block : res.getLIR().getControlFlowGraph().getBlocks()) {
-                try (Indent indent0 = Debug.logAndIndent("block: %s", block)) {
+                try (Indent indent0 = debug.logAndIndent("block: %s", block)) {
                     for (LIRInstruction inst : res.getLIR().getLIRforBlock(block)) {
-                        try (Indent indent1 = Debug.logAndIndent("Inst: %d: %s", inst.id(), inst)) {
+                        try (Indent indent1 = debug.logAndIndent("Inst: %d: %s", inst.id(), inst)) {
                             inst.forEachAlive(updateProc);
                             inst.forEachInput(updateProc);
                             inst.forEachOutput(updateProc);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/StackSlotAllocatorUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/stackslotalloc/StackSlotAllocatorUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,8 @@
  */
 package org.graalvm.compiler.lir.stackslotalloc;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.VirtualStackSlot;
 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
 
@@ -38,16 +38,16 @@
     /**
      * The number of allocated stack slots.
      */
-    public static DebugCounter allocatedSlots = Debug.counter("StackSlotAllocator[allocatedSlots]");
+    public static CounterKey allocatedSlots = DebugContext.counter("StackSlotAllocator[allocatedSlots]");
     /**
      * The number of reused stack slots.
      */
-    public static DebugCounter reusedSlots = Debug.counter("StackSlotAllocator[reusedSlots]");
+    public static CounterKey reusedSlots = DebugContext.counter("StackSlotAllocator[reusedSlots]");
     /**
      * The size (in bytes) required for all allocated stack slots. Note that this number corresponds
      * to the actual frame size and might include alignment.
      */
-    public static DebugCounter allocatedFramesize = Debug.counter("StackSlotAllocator[AllocatedFramesize]");
+    public static CounterKey allocatedFramesize = DebugContext.counter("StackSlotAllocator[AllocatedFramesize]");
     /** The size (in bytes) required for all virtual stack slots. */
-    public static DebugCounter virtualFramesize = Debug.counter("StackSlotAllocator[VirtualFramesize]");
+    public static CounterKey virtualFramesize = DebugContext.counter("StackSlotAllocator[VirtualFramesize]");
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopFullUnrollPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopFullUnrollPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,8 @@
  */
 package org.graalvm.compiler.loop.phases;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopPolicies;
 import org.graalvm.compiler.loop.LoopsData;
@@ -33,7 +33,7 @@
 
 public class LoopFullUnrollPhase extends LoopPhase<LoopPolicies> {
 
-    private static final DebugCounter FULLY_UNROLLED_LOOPS = Debug.counter("FullUnrolls");
+    private static final CounterKey FULLY_UNROLLED_LOOPS = DebugContext.counter("FullUnrolls");
     private final CanonicalizerPhase canonicalizer;
 
     public LoopFullUnrollPhase(CanonicalizerPhase canonicalizer, LoopPolicies policies) {
@@ -43,6 +43,7 @@
 
     @Override
     protected void run(StructuredGraph graph, PhaseContext context) {
+        DebugContext debug = graph.getDebug();
         if (graph.hasLoops()) {
             boolean peeled;
             do {
@@ -51,10 +52,10 @@
                 dataCounted.detectedCountedLoops();
                 for (LoopEx loop : dataCounted.countedLoops()) {
                     if (getPolicies().shouldFullUnroll(loop)) {
-                        Debug.log("FullUnroll %s", loop);
+                        debug.log("FullUnroll %s", loop);
                         LoopTransformations.fullUnroll(loop, context, canonicalizer);
-                        FULLY_UNROLLED_LOOPS.increment();
-                        Debug.dump(Debug.DETAILED_LEVEL, graph, "FullUnroll %s", loop);
+                        FULLY_UNROLLED_LOOPS.increment(debug);
+                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "FullUnroll %s", loop);
                         peeled = true;
                         break;
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012, 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.loop.phases;
+
+import org.graalvm.compiler.graph.Graph;
+import org.graalvm.compiler.loop.LoopEx;
+import org.graalvm.compiler.loop.LoopPolicies;
+import org.graalvm.compiler.loop.LoopsData;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener;
+import org.graalvm.compiler.phases.tiers.PhaseContext;
+
+public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
+
+    private final CanonicalizerPhase canonicalizer;
+
+    public LoopPartialUnrollPhase(LoopPolicies policies, CanonicalizerPhase canonicalizer) {
+        super(policies);
+        this.canonicalizer = canonicalizer;
+    }
+
+    @Override
+    @SuppressWarnings("try")
+    protected void run(StructuredGraph graph, PhaseContext context) {
+        if (graph.hasLoops()) {
+            HashSetNodeEventListener listener = new HashSetNodeEventListener();
+            try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
+                boolean changed = true;
+                while (changed) {
+                    LoopsData dataCounted = new LoopsData(graph);
+                    dataCounted.detectedCountedLoops();
+                    changed = false;
+                    for (LoopEx loop : dataCounted.countedLoops()) {
+                        if (!LoopTransformations.isUnrollableLoop(loop)) {
+                            continue;
+                        }
+                        if (getPolicies().shouldPartiallyUnroll(loop)) {
+                            if (loop.loopBegin().isSimpleLoop()) {
+                                // First perform the pre/post transformation and do the partial
+                                // unroll when we come around again.
+                                LoopTransformations.insertPrePostLoops(loop, graph);
+                                changed = true;
+                            } else {
+                                changed |= LoopTransformations.partialUnroll(loop, graph);
+                            }
+                        }
+                    }
+                    dataCounted.deleteUnusedNodes();
+                }
+            }
+            if (!listener.getNodes().isEmpty()) {
+                canonicalizer.applyIncremental(graph, context, listener.getNodes());
+                listener.getNodes().clear();
+            }
+        }
+    }
+
+    @Override
+    public boolean checkContract() {
+        return false;
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.loop.phases;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopPolicies;
 import org.graalvm.compiler.loop.LoopsData;
@@ -38,19 +38,20 @@
     @Override
     @SuppressWarnings("try")
     protected void run(StructuredGraph graph, PhaseContext context) {
+        DebugContext debug = graph.getDebug();
         if (graph.hasLoops()) {
             LoopsData data = new LoopsData(graph);
-            try (Debug.Scope s = Debug.scope("peeling", data.getCFG())) {
+            try (DebugContext.Scope s = debug.scope("peeling", data.getCFG())) {
                 for (LoopEx loop : data.outerFirst()) {
                     if (getPolicies().shouldPeel(loop, data.getCFG(), context.getMetaAccess())) {
-                        Debug.log("Peeling %s", loop);
+                        debug.log("Peeling %s", loop);
                         LoopTransformations.peel(loop);
-                        Debug.dump(Debug.DETAILED_LEVEL, graph, "Peeling %s", loop);
+                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "Peeling %s", loop);
                     }
                 }
                 data.deleteUnusedNodes();
             } catch (Throwable t) {
-                throw Debug.handle(t);
+                throw debug.handle(t);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,25 +23,56 @@
 package org.graalvm.compiler.loop.phases;
 
 import static org.graalvm.compiler.core.common.GraalOptions.MaximumDesiredSize;
+import static org.graalvm.compiler.loop.MathUtil.add;
+import static org.graalvm.compiler.loop.MathUtil.sub;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import org.graalvm.compiler.core.common.RetryableBailoutException;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Graph.Mark;
-import org.graalvm.compiler.core.common.RetryableBailoutException;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.graph.NodeWorkList;
 import org.graalvm.compiler.graph.Position;
+import org.graalvm.compiler.loop.BasicInductionVariable;
+import org.graalvm.compiler.loop.CountedLoopInfo;
+import org.graalvm.compiler.loop.DerivedInductionVariable;
+import org.graalvm.compiler.loop.InductionVariable;
+import org.graalvm.compiler.loop.InductionVariable.Direction;
 import org.graalvm.compiler.loop.LoopEx;
+import org.graalvm.compiler.loop.LoopFragmentInside;
 import org.graalvm.compiler.loop.LoopFragmentWhole;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.AbstractEndNode;
+import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.BeginNode;
+import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
+import org.graalvm.compiler.nodes.EndNode;
+import org.graalvm.compiler.nodes.FixedNode;
+import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.LoopEndNode;
+import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.ValuePhiNode;
+import org.graalvm.compiler.nodes.calc.AddNode;
+import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
+import org.graalvm.compiler.nodes.calc.CompareNode;
+import org.graalvm.compiler.nodes.calc.ConditionalNode;
+import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
+import org.graalvm.compiler.nodes.calc.SubNode;
 import org.graalvm.compiler.nodes.extended.SwitchNode;
+import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 
@@ -57,7 +88,7 @@
     }
 
     public static void fullUnroll(LoopEx loop, PhaseContext context, CanonicalizerPhase canonicalizer) {
-        // assert loop.isCounted(); //TODO (gd) strenghten : counted with known trip count
+        // assert loop.isCounted(); //TODO (gd) strengthen : counted with known trip count
         LoopBeginNode loopBegin = loop.loopBegin();
         StructuredGraph graph = loopBegin.graph();
         int initialNodeCount = graph.getNodeCount();
@@ -123,6 +154,511 @@
         // TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms)
     }
 
+    public static boolean partialUnroll(LoopEx loop, StructuredGraph graph) {
+        assert loop.loopBegin().isMainLoop();
+        graph.getDebug().log("LoopPartialUnroll %s", loop);
+        boolean changed = false;
+        CountedLoopInfo mainCounted = loop.counted();
+        LoopBeginNode mainLoopBegin = loop.loopBegin();
+        InductionVariable iv = mainCounted.getCounter();
+        IfNode mainLimit = mainCounted.getLimitTest();
+        LogicNode ifTest = mainLimit.condition();
+        CompareNode compareNode = (CompareNode) ifTest;
+        ValueNode compareBound = null;
+        ValueNode curPhi = iv.valueNode();
+        if (compareNode.getX() == curPhi) {
+            compareBound = compareNode.getY();
+        } else if (compareNode.getY() == curPhi) {
+            compareBound = compareNode.getX();
+        }
+        LoopFragmentInside newSegment = loop.inside().duplicate();
+        newSegment.insertWithinAfter(loop);
+        graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication inside %s", mainLoopBegin);
+        ValueNode inductionNode = iv.valueNode();
+        Node newStrideNode = null;
+        for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
+            Node segmentOrigOp = null;
+            Node replacementOp = null;
+            changed = false;
+            // Rework each phi with a loop carried dependence
+            for (Node phiUsage : mainPhiNode.usages()) {
+                if (!loop.isOutsideLoop(phiUsage)) {
+                    for (int i = 1; i < mainPhiNode.valueCount(); i++) {
+                        ValueNode v = mainPhiNode.valueAt(i);
+                        if (mainPhiNode != inductionNode) {
+                            if (closureOnPhiInputToPhiUse(v, phiUsage, loop, graph)) {
+                                segmentOrigOp = v;
+                                Node node = newSegment.getDuplicatedNode(v);
+                                replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, v, newSegment);
+
+                                // Update the induction phi with new stride node
+                                mainPhiNode.setValueAt(i, (ValueNode) node);
+                                // This is for induction variables not referenced in the loop body
+                                if (inductionNode == v) {
+                                    newStrideNode = node;
+                                }
+                                changed = true;
+                                break;
+                            }
+                        } else if (v == phiUsage) {
+                            segmentOrigOp = phiUsage;
+                            Node node = newSegment.getDuplicatedNode(phiUsage);
+                            newStrideNode = node;
+                            replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, phiUsage, newSegment);
+
+                            // Update the induction phi with new stride node
+                            mainPhiNode.setValueAt(i, (ValueNode) node);
+                            changed = true;
+                            break;
+                        }
+                    }
+                }
+                if (changed) {
+                    break;
+                }
+            }
+
+            if (changed) {
+                // Patch the new segments induction uses of replacementOp with the old stride node
+                for (Node usage : mainPhiNode.usages()) {
+                    if (usage != segmentOrigOp) {
+                        if (!loop.isOutsideLoop(usage)) {
+                            Node node = newSegment.getDuplicatedNode(usage);
+                            if (node instanceof CompareNode) {
+                                continue;
+                            }
+                            node.replaceFirstInput(replacementOp, segmentOrigOp);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (changed && newStrideNode == null) {
+            throw GraalError.shouldNotReachHere("Can't find stride node");
+        }
+        if (newStrideNode != null) {
+            // If merge the duplicate code into the loop and remove redundant code
+            placeNewSegmentAndCleanup(mainCounted, mainLoopBegin, newSegment);
+            int unrollFactor = mainLoopBegin.getUnrollFactor();
+            // First restore the old pattern of the loop exit condition so we can update it one way
+            if (unrollFactor > 1) {
+                if (compareBound instanceof SubNode) {
+                    SubNode newLimit = (SubNode) compareBound;
+                    ValueNode oldcompareBound = newLimit.getX();
+                    compareNode.replaceFirstInput(newLimit, oldcompareBound);
+                    newLimit.safeDelete();
+                    compareBound = oldcompareBound;
+                } else if (compareBound instanceof AddNode) {
+                    AddNode newLimit = (AddNode) compareBound;
+                    ValueNode oldcompareBound = newLimit.getX();
+                    compareNode.replaceFirstInput(newLimit, oldcompareBound);
+                    newLimit.safeDelete();
+                    compareBound = oldcompareBound;
+                }
+            }
+            unrollFactor *= 2;
+            mainLoopBegin.setUnrollFactor(unrollFactor);
+            // Reset stride to include new segment in loop control.
+            long oldStride = iv.constantStride() * 2;
+            // Now update the induction op and the exit condition
+            if (iv instanceof BasicInductionVariable) {
+                BasicInductionVariable biv = (BasicInductionVariable) iv;
+                BinaryArithmeticNode<?> newOp = (BinaryArithmeticNode<?>) newStrideNode;
+                Stamp strideStamp = newOp.stamp();
+                ConstantNode newStrideVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, oldStride));
+                newOp.setY(newStrideVal);
+                biv.setOP(newOp);
+                // Now use the current unrollFactor to update the exit condition to power of two
+                if (unrollFactor > 1) {
+                    if (iv.direction() == Direction.Up) {
+                        int modulas = (unrollFactor - 1);
+                        ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
+                        ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
+                        compareNode.replaceFirstInput(compareBound, newLimit);
+                    } else if (iv.direction() == Direction.Down) {
+                        int modulas = (unrollFactor - 1);
+                        ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
+                        ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
+                        compareNode.replaceFirstInput(compareBound, newLimit);
+                    }
+                }
+                mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
+            }
+            changed = true;
+        }
+        if (changed) {
+            graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
+        }
+        return changed;
+    }
+
+    private static Node updateUnrollSegmentValue(PhiNode mainPhiNode, Node inductionNode, Node phiUsage, Node patchNode, LoopFragmentInside newSegment) {
+        Node node = newSegment.getDuplicatedNode(phiUsage);
+        assert node != null : phiUsage;
+        Node replacementOp = null;
+        int inputCnt = 0;
+        for (Node input : phiUsage.inputs()) {
+            inputCnt++;
+            if (input == mainPhiNode) {
+                break;
+            }
+        }
+        int newInputCnt = 0;
+        for (Node input : node.inputs()) {
+            newInputCnt++;
+            if (newInputCnt == inputCnt) {
+                replacementOp = input;
+                if (mainPhiNode == inductionNode) {
+                    node.replaceFirstInput(input, mainPhiNode);
+                } else {
+                    node.replaceFirstInput(input, patchNode);
+                }
+                break;
+            }
+        }
+        return replacementOp;
+    }
+
+    private static boolean closureOnPhiInputToPhiUse(Node inNode, Node usage, LoopEx loop, StructuredGraph graph) {
+        NodeWorkList nodes = graph.createNodeWorkList();
+        nodes.add(inNode);
+        // Now walk from the inNode to usage if we can find it else we do not have closure
+        for (Node node : nodes) {
+            if (node == usage) {
+                return true;
+            }
+            for (Node input : node.inputs()) {
+                if (!loop.isOutsideLoop(input)) {
+                    if (input != usage) {
+                        nodes.add(input);
+                    } else {
+                        return true;
+                        // For any reason if we have completed a closure, stop processing more
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static void placeNewSegmentAndCleanup(CountedLoopInfo mainCounted, LoopBeginNode mainLoopBegin, LoopFragmentInside newSegment) {
+        // Discard the segment entry and its flow, after if merging it into the loop
+        StructuredGraph graph = mainLoopBegin.graph();
+        IfNode loopTest = mainCounted.getLimitTest();
+        IfNode newSegmentTest = newSegment.getDuplicatedNode(loopTest);
+        AbstractBeginNode trueSuccessor = loopTest.trueSuccessor();
+        AbstractBeginNode falseSuccessor = loopTest.falseSuccessor();
+        FixedNode firstNode;
+        boolean codeInTrueSide = false;
+        if (trueSuccessor == mainCounted.getBody()) {
+            firstNode = trueSuccessor.next();
+            codeInTrueSide = true;
+        } else {
+            assert (falseSuccessor == mainCounted.getBody());
+            firstNode = falseSuccessor.next();
+        }
+        trueSuccessor = newSegmentTest.trueSuccessor();
+        falseSuccessor = newSegmentTest.falseSuccessor();
+        for (Node usage : falseSuccessor.anchored().snapshot()) {
+            usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor());
+        }
+        for (Node usage : trueSuccessor.anchored().snapshot()) {
+            usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor());
+        }
+        AbstractBeginNode startBlockNode;
+        if (codeInTrueSide) {
+            startBlockNode = trueSuccessor;
+        } else {
+            graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before");
+            startBlockNode = falseSuccessor;
+        }
+        FixedNode lastNode = getBlockEnd(startBlockNode);
+        LoopEndNode loopEndNode = getSingleLoopEndFromLoop(mainLoopBegin);
+        FixedNode lastCodeNode = (FixedNode) loopEndNode.predecessor();
+        FixedNode newSegmentFirstNode = newSegment.getDuplicatedNode(firstNode);
+        FixedNode newSegmentLastNode = newSegment.getDuplicatedNode(lastCodeNode);
+        graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
+        if (firstNode instanceof LoopEndNode) {
+            GraphUtil.killCFG(newSegment.getDuplicatedNode(mainLoopBegin));
+        } else {
+            newSegmentLastNode.clearSuccessors();
+            startBlockNode.setNext(lastNode);
+            lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
+            newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
+            FixedWithNextNode oldLastNode = (FixedWithNextNode) lastCodeNode;
+            oldLastNode.setNext(newSegmentFirstNode);
+            FixedWithNextNode newLastNode = (FixedWithNextNode) newSegmentLastNode;
+            newLastNode.setNext(loopEndNode);
+            startBlockNode.clearSuccessors();
+            lastNode.safeDelete();
+            Node newSegmentTestStart = newSegmentTest.predecessor();
+            LogicNode newSegmentIfTest = newSegmentTest.condition();
+            newSegmentTestStart.clearSuccessors();
+            newSegmentTest.safeDelete();
+            newSegmentIfTest.safeDelete();
+            trueSuccessor.safeDelete();
+            falseSuccessor.safeDelete();
+            newSegmentTestStart.safeDelete();
+        }
+        graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment");
+    }
+
+    // This function splits candidate loops into pre, main and post loops,
+    // dividing the iteration space to facilitate the majority of iterations
+    // being executed in a main loop, which will have RCE implemented upon it.
+    // The initial loop form is constrained to single entry/exit, but can have
+    // flow. The translation looks like:
+    //
+    //  @formatter:off
+    //
+    //       (Simple Loop entry)                   (Pre Loop Entry)
+    //                |                                  |
+    //         (LoopBeginNode)                    (LoopBeginNode)
+    //                |                                  |
+    //       (Loop Control Test)<------   ==>  (Loop control Test)<------
+    //         /               \       \         /               \       \
+    //    (Loop Exit)      (Loop Body) |    (Loop Exit)      (Loop Body) |
+    //        |                |       |        |                |       |
+    // (continue code)     (Loop End)  |  if (M < length)*   (Loop End)  |
+    //                         \       /       /      \           \      /
+    //                          ----->        /       |            ----->
+    //                                       /  if ( ... )*
+    //                                      /     /       \
+    //                                     /     /         \
+    //                                    /     /           \
+    //                                   |     /     (Main Loop Entry)
+    //                                   |    |             |
+    //                                   |    |      (LoopBeginNode)
+    //                                   |    |             |
+    //                                   |    |     (Loop Control Test)<------
+    //                                   |    |      /               \        \
+    //                                   |    |  (Loop Exit)      (Loop Body) |
+    //                                    \   \      |                |       |
+    //                                     \   \     |            (Loop End)  |
+    //                                      \   \    |                \       /
+    //                                       \   \   |                 ------>
+    //                                        \   \  |
+    //                                      (Main Loop Merge)*
+    //                                               |
+    //                                      (Post Loop Entry)
+    //                                               |
+    //                                        (LoopBeginNode)
+    //                                               |
+    //                                       (Loop Control Test)<-----
+    //                                        /               \       \
+    //                                    (Loop Exit)     (Loop Body) |
+    //                                        |               |       |
+    //                                 (continue code)    (Loop End)  |
+    //                                                         \      /
+    //                                                          ----->
+    //
+    // Key: "*" = optional.
+    // @formatter:on
+    //
+    // The value "M" is the maximal value of the loop trip for the original
+    // loop. The value of "length" is applicable to the number of arrays found
+    // in the loop but is reduced if some or all of the arrays are known to be
+    // the same length as "M". The maximum number of tests can be equal to the
+    // number of arrays in the loop, where multiple instances of an array are
+    // subsumed into a single test for that arrays length.
+    //
+    // If the optional main loop entry tests are absent, the Pre Loop exit
+    // connects to the Main loops entry and there is no merge hanging off the
+    // main loops exit to converge flow from said tests. All split use data
+    // flow is mitigated through phi(s) in the main merge if present and
+    // passed through the main and post loop phi(s) from the originating pre
+    // loop with final phi(s) and data flow patched to the "continue code".
+    // The pre loop is constrained to one iteration for now and will likely
+    // be updated to produce vector alignment if applicable.
+
+    public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) {
+        graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
+        LoopFragmentWhole preLoop = loop.whole();
+        CountedLoopInfo preCounted = preLoop.loop().counted();
+        IfNode preLimit = preCounted.getLimitTest();
+        if (preLimit != null) {
+            LoopBeginNode preLoopBegin = loop.loopBegin();
+            InductionVariable preIv = preCounted.getCounter();
+            LoopExitNode preLoopExitNode = getSingleExitFromLoop(preLoopBegin);
+            FixedNode continuationNode = preLoopExitNode.next();
+
+            // Each duplication is inserted after the original, ergo create the post loop first
+            LoopFragmentWhole mainLoop = preLoop.duplicate();
+            LoopFragmentWhole postLoop = preLoop.duplicate();
+            preLoopBegin.incrementSplits();
+            preLoopBegin.incrementSplits();
+            preLoopBegin.setPreLoop();
+            graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication");
+            LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin);
+            mainLoopBegin.setMainLoop();
+            LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin);
+            postLoopBegin.setPostLoop();
+
+            EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
+            AbstractMergeNode postMergeNode = postEndNode.merge();
+            LoopExitNode postLoopExitNode = getSingleExitFromLoop(postLoopBegin);
+
+            // Update the main loop phi initialization to carry from the pre loop
+            for (PhiNode prePhiNode : preLoopBegin.phis()) {
+                PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
+                mainPhiNode.setValueAt(0, prePhiNode);
+            }
+
+            EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin);
+            AbstractMergeNode mainMergeNode = mainEndNode.merge();
+            AbstractEndNode postEntryNode = postLoopBegin.forwardEnd();
+
+            // In the case of no Bounds tests, we just flow right into the main loop
+            AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
+            LoopExitNode mainLoopExitNode = getSingleExitFromLoop(mainLoopBegin);
+            mainLoopExitNode.setNext(mainLandingNode);
+            preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
+
+            // Add and update any phi edges as per merge usage as needed and update usages
+            processPreLoopPhis(loop, mainLoop, postLoop);
+            continuationNode.predecessor().clearSuccessors();
+            postLoopExitNode.setNext(continuationNode);
+            cleanupMerge(postMergeNode, postLoopExitNode);
+            cleanupMerge(mainMergeNode, mainLandingNode);
+
+            // Change the preLoop to execute one iteration for now
+            updateMainLoopLimit(preLimit, preIv, mainLoop);
+            updatePreLoopLimit(preLimit, preIv, preCounted);
+            preLoopBegin.setLoopFrequency(1);
+            mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
+            postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
+        }
+        graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
+    }
+
+    /**
+     * Cleanup the merge and remove the predecessors too.
+     */
+    private static void cleanupMerge(AbstractMergeNode mergeNode, AbstractBeginNode landingNode) {
+        for (EndNode end : mergeNode.cfgPredecessors().snapshot()) {
+            mergeNode.removeEnd(end);
+            end.safeDelete();
+        }
+        mergeNode.prepareDelete(landingNode);
+        mergeNode.safeDelete();
+    }
+
+    private static void processPreLoopPhis(LoopEx preLoop, LoopFragmentWhole mainLoop, LoopFragmentWhole postLoop) {
+        // process phis for the post loop
+        LoopBeginNode preLoopBegin = preLoop.loopBegin();
+        for (PhiNode prePhiNode : preLoopBegin.phis()) {
+            PhiNode postPhiNode = postLoop.getDuplicatedNode(prePhiNode);
+            PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
+            postPhiNode.setValueAt(0, mainPhiNode);
+
+            // Build a work list to update the pre loop phis to the post loops phis
+            for (Node usage : prePhiNode.usages().snapshot()) {
+                if (usage == mainPhiNode) {
+                    continue;
+                }
+                if (preLoop.isOutsideLoop(usage)) {
+                    usage.replaceFirstInput(prePhiNode, postPhiNode);
+                }
+            }
+        }
+        for (Node node : preLoop.inside().nodes()) {
+            for (Node externalUsage : node.usages().snapshot()) {
+                if (preLoop.isOutsideLoop(externalUsage)) {
+                    Node postUsage = postLoop.getDuplicatedNode(node);
+                    assert postUsage != null;
+                    externalUsage.replaceFirstInput(node, postUsage);
+                }
+            }
+        }
+    }
+
+    private static LoopExitNode getSingleExitFromLoop(LoopBeginNode curLoopBegin) {
+        assert curLoopBegin.loopExits().count() == 1;
+        return curLoopBegin.loopExits().first();
+    }
+
+    private static LoopEndNode getSingleLoopEndFromLoop(LoopBeginNode curLoopBegin) {
+        assert curLoopBegin.loopEnds().count() == 1;
+        return curLoopBegin.loopEnds().first();
+    }
+
+    /**
+     * Find the end of the block following the LoopExit.
+     */
+    private static EndNode getBlockEndAfterLoopExit(LoopBeginNode curLoopBegin) {
+        FixedNode node = getSingleExitFromLoop(curLoopBegin).next();
+        // Find the last node after the exit blocks starts
+        return getBlockEnd(node);
+    }
+
+    private static EndNode getBlockEnd(FixedNode node) {
+        FixedNode curNode = node;
+        while (curNode instanceof FixedWithNextNode) {
+            curNode = ((FixedWithNextNode) curNode).next();
+        }
+        return (EndNode) curNode;
+    }
+
+    private static void updateMainLoopLimit(IfNode preLimit, InductionVariable preIv, LoopFragmentWhole mainLoop) {
+        // Update the main loops limit test to be different than the post loop
+        StructuredGraph graph = preLimit.graph();
+        IfNode mainLimit = mainLoop.getDuplicatedNode(preLimit);
+        LogicNode ifTest = mainLimit.condition();
+        CompareNode compareNode = (CompareNode) ifTest;
+        ValueNode prePhi = preIv.valueNode();
+        ValueNode mainPhi = mainLoop.getDuplicatedNode(prePhi);
+        ValueNode preStride = preIv.strideNode();
+        ValueNode mainStride;
+        if (preStride instanceof ConstantNode) {
+            mainStride = preStride;
+        } else {
+            mainStride = mainLoop.getDuplicatedNode(preStride);
+        }
+        // Fetch the bounds to pose lowering the range by one
+        ValueNode ub = null;
+        if (compareNode.getX() == mainPhi) {
+            ub = compareNode.getY();
+        } else if (compareNode.getY() == mainPhi) {
+            ub = compareNode.getX();
+        } else {
+            throw GraalError.shouldNotReachHere();
+        }
+
+        // Preloop always performs at least once iteration, so remove that from the main loop.
+        ValueNode newLimit = sub(graph, ub, mainStride);
+
+        // Re-wire the condition with the new limit
+        compareNode.replaceFirstInput(ub, newLimit);
+    }
+
+    private static void updatePreLoopLimit(IfNode preLimit, InductionVariable preIv, CountedLoopInfo preCounted) {
+        // Update the pre loops limit test
+        StructuredGraph graph = preLimit.graph();
+        LogicNode ifTest = preLimit.condition();
+        CompareNode compareNode = (CompareNode) ifTest;
+        ValueNode prePhi = preIv.valueNode();
+        // Make new limit one iteration
+        ValueNode initIv = preCounted.getStart();
+        ValueNode newLimit = add(graph, initIv, preIv.strideNode());
+
+        // Fetch the variable we are not replacing and configure the one we are
+        ValueNode ub;
+        if (compareNode.getX() == prePhi) {
+            ub = compareNode.getY();
+        } else if (compareNode.getY() == prePhi) {
+            ub = compareNode.getX();
+        } else {
+            throw GraalError.shouldNotReachHere();
+        }
+        // Re-wire the condition with the new limit
+        if (preIv.direction() == Direction.Up) {
+            compareNode.replaceFirstInput(ub, graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(newLimit, ub)), newLimit, ub)));
+        } else {
+            compareNode.replaceFirstInput(ub, graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(ub, newLimit)), newLimit, ub)));
+        }
+    }
+
     public static List<ControlSplitNode> findUnswitchable(LoopEx loop) {
         List<ControlSplitNode> controls = null;
         ValueNode invariantValue = null;
@@ -155,4 +691,45 @@
         }
         return controls;
     }
+
+    public static boolean isUnrollableLoop(LoopEx loop) {
+        if (!loop.isCounted()) {
+            return false;
+        }
+        LoopBeginNode loopBegin = loop.loopBegin();
+        boolean isCanonical = false;
+        if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) {
+            // Flow-less loops to partial unroll for now. 3 blocks corresponds to an if that either
+            // exits or continues the loop. There might be fixed and floating work within the loop
+            // as well.
+            if (loop.loop().getBlocks().size() < 3) {
+                isCanonical = true;
+            }
+        }
+        if (!isCanonical) {
+            return false;
+        }
+        for (ValuePhiNode phi : loopBegin.valuePhis()) {
+            if (phi.usages().filter(x -> loopBegin.isPhiAtMerge(x)).isNotEmpty()) {
+                // Filter out Phis which reference Phis at the same merge until the duplication
+                // logic handles it properly.
+                return false;
+            }
+            InductionVariable iv = loop.getInductionVariables().get(phi);
+            if (iv == null) {
+                continue;
+            }
+            if (iv instanceof DerivedInductionVariable) {
+                return false;
+            } else if (iv instanceof BasicInductionVariable) {
+                BasicInductionVariable biv = (BasicInductionVariable) iv;
+                if (!biv.isConstantStride()) {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopUnswitchingPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopUnswitchingPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,8 +25,8 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopPolicies;
@@ -36,9 +36,9 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 
 public class LoopUnswitchingPhase extends ContextlessLoopPhase<LoopPolicies> {
-    private static final DebugCounter UNSWITCHED = Debug.counter("Unswitched");
-    private static final DebugCounter UNSWITCH_CANDIDATES = Debug.counter("UnswitchCandidates");
-    private static final DebugCounter UNSWITCH_EARLY_REJECTS = Debug.counter("UnswitchEarlyRejects");
+    private static final CounterKey UNSWITCHED = DebugContext.counter("Unswitched");
+    private static final CounterKey UNSWITCH_CANDIDATES = DebugContext.counter("UnswitchCandidates");
+    private static final CounterKey UNSWITCH_EARLY_REJECTS = DebugContext.counter("UnswitchEarlyRejects");
 
     public LoopUnswitchingPhase(LoopPolicies policies) {
         super(policies);
@@ -46,6 +46,7 @@
 
     @Override
     protected void run(StructuredGraph graph) {
+        DebugContext debug = graph.getDebug();
         if (graph.hasLoops()) {
             boolean unswitched;
             do {
@@ -55,20 +56,20 @@
                     if (getPolicies().shouldTryUnswitch(loop)) {
                         List<ControlSplitNode> controlSplits = LoopTransformations.findUnswitchable(loop);
                         if (controlSplits != null) {
-                            UNSWITCH_CANDIDATES.increment();
+                            UNSWITCH_CANDIDATES.increment(debug);
                             if (getPolicies().shouldUnswitch(loop, controlSplits)) {
-                                if (Debug.isLogEnabled()) {
+                                if (debug.isLogEnabled()) {
                                     logUnswitch(loop, controlSplits);
                                 }
                                 LoopTransformations.unswitch(loop, controlSplits);
-                                Debug.dump(Debug.DETAILED_LEVEL, graph, "After unswitch %s", controlSplits);
-                                UNSWITCHED.increment();
+                                debug.dump(DebugContext.DETAILED_LEVEL, graph, "After unswitch %s", controlSplits);
+                                UNSWITCHED.increment(debug);
                                 unswitched = true;
                                 break;
                             }
                         }
                     } else {
-                        UNSWITCH_EARLY_REJECTS.increment();
+                        UNSWITCH_EARLY_REJECTS.increment(debug);
                     }
                 }
             } while (unswitched);
@@ -89,7 +90,7 @@
             }
             sb.append("]");
         }
-        Debug.log("%s", sb);
+        loop.entryPoint().getDebug().log("%s", sb);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/ReassociateInvariantPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/ReassociateInvariantPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,7 @@
  */
 package org.graalvm.compiler.loop.phases;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopsData;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -36,12 +35,13 @@
     protected void run(StructuredGraph graph) {
         if (graph.hasLoops()) {
             final LoopsData dataReassociate = new LoopsData(graph);
-            try (Scope s = Debug.scope("ReassociateInvariants")) {
+            DebugContext debug = graph.getDebug();
+            try (DebugContext.Scope s = debug.scope("ReassociateInvariants")) {
                 for (LoopEx loop : dataReassociate.loops()) {
                     loop.reassociateInvariants();
                 }
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
             dataReassociate.deleteUnusedNodes();
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,139 @@
+/*
+ * 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.loop.test;
+
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.graph.iterators.NodeIterable;
+import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class LoopPartialUnrollTest extends GraalCompilerTest {
+
+    @Override
+    protected boolean checkLowTierGraph(StructuredGraph graph) {
+        NodeIterable<LoopBeginNode> loops = graph.getNodes().filter(LoopBeginNode.class);
+        for (LoopBeginNode loop : loops) {
+            if (loop.isMainLoop()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static long testMultiplySnippet(int arg) {
+        long r = 1;
+        for (int i = 0; branchProbability(0.99, i < arg); i++) {
+            r *= i;
+        }
+        return r;
+    }
+
+    @Test
+    public void testMultiply() {
+        test("testMultiplySnippet", 9);
+    }
+
+    public static int testNestedSumSnippet(int d) {
+        int c = 0;
+        for (int i = 0; i < d; i++) {
+            for (int j = 0; branchProbability(0.99, j < i); j++) {
+                c += j & 0x3;
+            }
+        }
+        return c;
+    }
+
+    @Test
+    public void testNestedSum() {
+        for (int i = 0; i < 1000; i++) {
+            test("testNestedSumSnippet", i);
+        }
+    }
+
+    public static int testSumDownSnippet(int d) {
+        int c = 0;
+        for (int j = d; branchProbability(0.99, j > -4); j--) {
+            c += j & 0x3;
+        }
+        return c;
+    }
+
+    @Test
+    public void testSumDown() {
+        test("testSumDownSnippet", 1);
+        for (int i = 0; i < 8; i++) {
+            test("testSumDownSnippet", i);
+        }
+    }
+
+    @Ignore("Phis which reference the backedge value of other Phis aren't handled properly")
+    @Test
+    public void testLoopCarried() {
+        test("testLoopCarriedSnippet", 1, 2);
+    }
+
+    public static int testLoopCarriedSnippet(int a, int b) {
+        int c = a;
+        int d = b;
+        for (int j = 0; j < a; j++) {
+            d = c;
+            c += 1;
+        }
+        return c + d;
+    }
+
+    public static long init = Runtime.getRuntime().totalMemory();
+    private int x;
+    private int z;
+
+    public int[] testComplexSnippet(int d) {
+        x = 3;
+        int y = 5;
+        z = 7;
+        for (int i = 0; i < d; i++) {
+            for (int j = 0; branchProbability(0.99, j < i); j++) {
+                z += x;
+            }
+            y = x ^ z;
+            if ((i & 4) == 0) {
+                z--;
+            } else if ((i & 8) == 0) {
+                Runtime.getRuntime().totalMemory();
+            }
+        }
+        return new int[]{x, y, z};
+    }
+
+    @Test
+    public void testComplex() {
+        for (int i = 0; i < 10; i++) {
+            test("testComplexSnippet", i);
+        }
+        test("testComplexSnippet", 10);
+        test("testComplexSnippet", 100);
+        test("testComplexSnippet", 1000);
+    }
+
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,8 +43,8 @@
 
     private final ValuePhiNode phi;
     private final ValueNode init;
-    private final ValueNode rawStride;
-    private final BinaryArithmeticNode<?> op;
+    private ValueNode rawStride;
+    private BinaryArithmeticNode<?> op;
 
     public BasicInductionVariable(LoopEx loop, ValuePhiNode phi, ValueNode init, ValueNode rawStride, BinaryArithmeticNode<?> op) {
         super(loop);
@@ -63,6 +63,11 @@
         return op;
     }
 
+    public void setOP(BinaryArithmeticNode<?> newOp) {
+        rawStride = newOp.getY();
+        op = newOp;
+    }
+
     @Override
     public Direction direction() {
         Stamp stamp = rawStride.stamp();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,6 +32,7 @@
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.GuardNode;
+import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
@@ -51,13 +52,15 @@
     private ValueNode end;
     private boolean oneOff;
     private AbstractBeginNode body;
+    private IfNode ifNode;
 
-    CountedLoopInfo(LoopEx loop, InductionVariable iv, ValueNode end, boolean oneOff, AbstractBeginNode body) {
+    CountedLoopInfo(LoopEx loop, InductionVariable iv, IfNode ifNode, ValueNode end, boolean oneOff, AbstractBeginNode body) {
         this.loop = loop;
         this.iv = iv;
         this.end = end;
         this.oneOff = oneOff;
         this.body = body;
+        this.ifNode = ifNode;
     }
 
     public ValueNode maxTripCountNode() {
@@ -164,6 +167,10 @@
         return end;
     }
 
+    public IfNode getLimitTest() {
+        return ifNode;
+    }
+
     public ValueNode getStart() {
         return iv.initNode();
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,8 +28,8 @@
 
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
@@ -37,17 +37,20 @@
 import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.VirtualState;
 import org.graalvm.compiler.nodes.VirtualState.VirtualClosure;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode;
 import org.graalvm.compiler.nodes.java.TypeSwitchNode;
 import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.options.OptionKey;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
 
@@ -59,6 +62,9 @@
     @Option(help = "", type = OptionType.Expert) public static final OptionKey<Integer> FullUnrollMaxNodes = new OptionKey<>(300);
     @Option(help = "", type = OptionType.Expert) public static final OptionKey<Integer> FullUnrollMaxIterations = new OptionKey<>(600);
     @Option(help = "", type = OptionType.Expert) public static final OptionKey<Integer> ExactFullUnrollMaxNodes = new OptionKey<>(1200);
+    @Option(help = "", type = OptionType.Expert) public static final OptionKey<Integer> ExactPartialUnrollMaxNodes = new OptionKey<>(200);
+
+    @Option(help = "", type = OptionType.Expert) public static final OptionKey<Integer> UnrollMaxIterations = new OptionKey<>(16);
 
     @Override
     public boolean shouldPeel(LoopEx loop, ControlFlowGraph cfg, MetaAccessProvider metaAccess) {
@@ -93,6 +99,48 @@
     }
 
     @Override
+    public boolean shouldPartiallyUnroll(LoopEx loop) {
+        if (!loop.isCounted()) {
+            return false;
+        }
+        OptionValues options = loop.entryPoint().getOptions();
+        LoopBeginNode loopBegin = loop.loopBegin();
+        int maxNodes = ExactPartialUnrollMaxNodes.getValue(options);
+        maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount()));
+        int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count());
+        int unrollFactor = loopBegin.getUnrollFactor();
+        if (unrollFactor == 1) {
+            double loopFrequency = loopBegin.loopFrequency();
+            if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) {
+                return false;
+            }
+            loopBegin.setLoopOrigFrequency(loopFrequency);
+        }
+        int maxUnroll = UnrollMaxIterations.getValue(options);
+        // Now correct size for the next unroll. UnrollMaxIterations == 1 means perform the
+        // pre/main/post transformation but don't actually unroll the main loop.
+        size += size;
+        if (maxUnroll == 1 && loopBegin.isSimpleLoop() || size <= maxNodes && unrollFactor < maxUnroll) {
+            // Will the next unroll fit?
+            if ((int) loopBegin.loopOrigFrequency() < (unrollFactor * 2)) {
+                return false;
+            }
+            // Check whether we're allowed to unroll this loop
+            for (Node node : loop.inside().nodes()) {
+                if (node instanceof ControlFlowAnchorNode) {
+                    return false;
+                }
+                if (node instanceof InvokeNode) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
     public boolean shouldTryUnswitch(LoopEx loop) {
         LoopBeginNode loopBegin = loop.loopBegin();
         double loopFrequency = loopBegin.loopFrequency();
@@ -113,13 +161,15 @@
     }
 
     private static class IsolatedInitialization {
-        static final DebugCounter UNSWITCH_SPLIT_WITH_PHIS = Debug.counter("UnswitchSplitWithPhis");
+        static final CounterKey UNSWITCH_SPLIT_WITH_PHIS = DebugContext.counter("UnswitchSplitWithPhis");
     }
 
     @Override
     public boolean shouldUnswitch(LoopEx loop, List<ControlSplitNode> controlSplits) {
         int phis = 0;
-        NodeBitMap branchNodes = loop.loopBegin().graph().createNodeBitMap();
+        StructuredGraph graph = loop.loopBegin().graph();
+        DebugContext debug = graph.getDebug();
+        NodeBitMap branchNodes = graph.createNodeBitMap();
         for (ControlSplitNode controlSplit : controlSplits) {
             for (Node successor : controlSplit.successors()) {
                 AbstractBeginNode branch = (AbstractBeginNode) successor;
@@ -128,7 +178,7 @@
             }
             Block postDomBlock = loop.loopsData().getCFG().blockFor(controlSplit).getPostdominator();
             if (postDomBlock != null) {
-                IsolatedInitialization.UNSWITCH_SPLIT_WITH_PHIS.increment();
+                IsolatedInitialization.UNSWITCH_SPLIT_WITH_PHIS.increment(debug);
                 phis += ((MergeNode) postDomBlock.getBeginNode()).phis().count();
             }
         }
@@ -140,7 +190,7 @@
         int maxDiff = LoopUnswitchTrivial.getValue(options) + (int) (LoopUnswitchFrequencyBoost.getValue(options) * (loopFrequency - 1.0 + phis));
 
         maxDiff = Math.min(maxDiff, LoopUnswitchMaxIncrease.getValue(options));
-        int remainingGraphSpace = MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount();
+        int remainingGraphSpace = MaximumDesiredSize.getValue(options) - graph.getNodeCount();
         maxDiff = Math.min(maxDiff, remainingGraphSpace);
 
         loop.loopBegin().stateAfter().applyToVirtual(stateNodesCount);
@@ -161,7 +211,7 @@
             actualDiff = actualDiff * copies;
         }
 
-        Debug.log("shouldUnswitch(%s, %s) : delta=%d (%.2f%% inside of branches), max=%d, f=%.2f, phis=%d -> %b", loop, controlSplits, actualDiff, (double) (inBranchTotal) / loopTotal * 100, maxDiff,
+        debug.log("shouldUnswitch(%s, %s) : delta=%d (%.2f%% inside of branches), max=%d, f=%.2f, phis=%d -> %b", loop, controlSplits, actualDiff, (double) (inBranchTotal) / loopTotal * 100, maxDiff,
                         loopFrequency, phis, actualDiff <= maxDiff);
         if (actualDiff <= maxDiff) {
             // check whether we're allowed to unswitch this loop
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,7 +29,7 @@
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
@@ -46,7 +46,6 @@
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
-import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -180,8 +179,9 @@
             }
             ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY());
             if (result != binary) {
-                if (Debug.isLogEnabled()) {
-                    Debug.log("%s : Reassociated %s into %s", graph.method().format("%H::%n"), binary, result);
+                DebugContext debug = graph.getDebug();
+                if (debug.isLogEnabled()) {
+                    debug.log("%s : Reassociated %s into %s", graph.method().format("%H::%n"), binary, result);
                 }
                 if (!result.isAlive()) {
                     assert !result.isDeleted();
@@ -211,7 +211,7 @@
             LogicNode ifTest = ifNode.condition();
             if (!(ifTest instanceof IntegerLessThanNode) && !(ifTest instanceof IntegerEqualsNode)) {
                 if (ifTest instanceof IntegerBelowNode) {
-                    Debug.log("Ignored potential Counted loop at %s with |<|", loopBegin);
+                    ifTest.getDebug().log("Ignored potential Counted loop at %s with |<|", loopBegin);
                 }
                 return false;
             }
@@ -286,7 +286,7 @@
                 default:
                     throw GraalError.shouldNotReachHere();
             }
-            counted = new CountedLoopInfo(this, iv, limit, oneOff, negated ? ifNode.falseSuccessor() : ifNode.trueSuccessor());
+            counted = new CountedLoopInfo(this, iv, ifNode, limit, oneOff, negated ? ifNode.falseSuccessor() : ifNode.trueSuccessor());
             return true;
         }
         return false;
@@ -298,7 +298,7 @@
 
     public void nodesInLoopBranch(NodeBitMap branchNodes, AbstractBeginNode branch) {
         EconomicSet<AbstractBeginNode> blocks = EconomicSet.create();
-        Collection<LoopExitNode> exits = new LinkedList<>();
+        Collection<AbstractBeginNode> exits = new LinkedList<>();
         Queue<Block> work = new LinkedList<>();
         ControlFlowGraph cfg = loopsData().getCFG();
         work.add(cfg.blockFor(branch));
@@ -306,7 +306,7 @@
             Block b = work.remove();
             if (loop().getExits().contains(b)) {
                 assert !exits.contains(b.getBeginNode());
-                exits.add((LoopExitNode) b.getBeginNode());
+                exits.add(b.getBeginNode());
             } else if (blocks.add(b.getBeginNode())) {
                 Block d = b.getDominatedSibling();
                 while (d != null) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Fri Jul 07 09:40:47 2017 -0700
@@ -131,6 +131,8 @@
 
     protected abstract DuplicationReplacement getDuplicationReplacement();
 
+    protected abstract void beforeDuplication();
+
     protected abstract void finishDuplication();
 
     protected void patchNodes(final DuplicationReplacement dataFix) {
@@ -162,6 +164,7 @@
             } else {
                 dr = null;
             }
+            beforeDuplication();
             NodeIterable<Node> nodesIterable = original().nodes();
             duplicationMap = graph().addDuplicates(nodesIterable, graph(), nodesIterable.count(), dr);
             finishDuplication();
@@ -175,13 +178,13 @@
         return computeNodes(graph, blocks, Collections.emptyList());
     }
 
-    protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<LoopExitNode> earlyExits) {
+    protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<AbstractBeginNode> earlyExits) {
         final NodeBitMap nodes = graph.createNodeBitMap();
         computeNodes(nodes, graph, blocks, earlyExits);
         return nodes;
     }
 
-    protected static void computeNodes(NodeBitMap nodes, Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<LoopExitNode> earlyExits) {
+    protected static void computeNodes(NodeBitMap nodes, Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<AbstractBeginNode> earlyExits) {
         for (AbstractBeginNode b : blocks) {
             if (b.isDeleted()) {
                 continue;
@@ -198,18 +201,22 @@
                 nodes.mark(n);
             }
         }
-        for (LoopExitNode earlyExit : earlyExits) {
+        for (AbstractBeginNode earlyExit : earlyExits) {
             if (earlyExit.isDeleted()) {
                 continue;
             }
 
-            FrameState stateAfter = earlyExit.stateAfter();
-            if (stateAfter != null) {
-                stateAfter.applyToVirtual(node -> nodes.mark(node));
-            }
             nodes.mark(earlyExit);
-            for (ProxyNode proxy : earlyExit.proxies()) {
-                nodes.mark(proxy);
+
+            if (earlyExit instanceof LoopExitNode) {
+                LoopExitNode loopExit = (LoopExitNode) earlyExit;
+                FrameState stateAfter = loopExit.stateAfter();
+                if (stateAfter != null) {
+                    stateAfter.applyToVirtual(node -> nodes.mark(node));
+                }
+                for (ProxyNode proxy : loopExit.proxies()) {
+                    nodes.mark(proxy);
+                }
             }
         }
 
@@ -302,22 +309,30 @@
         };
     }
 
-    public static NodeIterable<LoopExitNode> toHirExits(final Iterable<Block> blocks) {
-        return new NodeIterable<LoopExitNode>() {
+    public static NodeIterable<AbstractBeginNode> toHirExits(final Iterable<Block> blocks) {
+        return new NodeIterable<AbstractBeginNode>() {
 
             @Override
-            public Iterator<LoopExitNode> iterator() {
+            public Iterator<AbstractBeginNode> iterator() {
                 final Iterator<Block> it = blocks.iterator();
-                return new Iterator<LoopExitNode>() {
+                return new Iterator<AbstractBeginNode>() {
 
                     @Override
                     public void remove() {
                         throw new UnsupportedOperationException();
                     }
 
+                    /**
+                     * Return the true LoopExitNode for this loop or the BeginNode for the block.
+                     */
                     @Override
-                    public LoopExitNode next() {
-                        return (LoopExitNode) it.next().getBeginNode();
+                    public AbstractBeginNode next() {
+                        Block next = it.next();
+                        LoopExitNode exit = next.getLoopExit();
+                        if (exit != null) {
+                            return exit;
+                        }
+                        return next.getBeginNode();
                     }
 
                     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Fri Jul 07 09:40:47 2017 -0700
@@ -50,8 +50,8 @@
 import org.graalvm.compiler.nodes.VirtualState.NodeClosure;
 import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 
 public class LoopFragmentInside extends LoopFragment {
 
@@ -74,6 +74,17 @@
         }
     };
 
+    private final DuplicationReplacement dataFixWithinAfter = new DuplicationReplacement() {
+
+        @Override
+        public Node replacement(Node oriInput) {
+            if (!(oriInput instanceof ValueNode)) {
+                return oriInput;
+            }
+            return primAfter((ValueNode) oriInput);
+        }
+    };
+
     public LoopFragmentInside(LoopEx loop) {
         super(loop);
     }
@@ -121,6 +132,12 @@
         end.setNext(loop.entryPoint());
     }
 
+    public void insertWithinAfter(LoopEx loop) {
+        assert this.isDuplicate() && this.original().loop() == loop;
+
+        patchNodes(dataFixWithinAfter);
+    }
+
     @Override
     public NodeBitMap nodes() {
         if (nodes == null) {
@@ -205,6 +222,11 @@
         // TODO (gd) ?
     }
 
+    @Override
+    protected void beforeDuplication() {
+        // Nothing to do
+    }
+
     private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) {
         PhiNode ret;
         if (phi instanceof ValuePhiNode) {
@@ -337,6 +359,24 @@
         }
     }
 
+    protected ValueNode primAfter(ValueNode b) {
+        assert isDuplicate();
+        LoopBeginNode loopBegin = original().loop().loopBegin();
+        if (loopBegin.isPhiAtMerge(b)) {
+            PhiNode phi = (PhiNode) b;
+            assert phi.valueCount() == 2;
+            return phi.valueAt(1);
+        } else if (nodesReady) {
+            ValueNode v = getDuplicatedNode(b);
+            if (v == null) {
+                return b;
+            }
+            return v;
+        } else {
+            return b;
+        }
+    }
+
     private AbstractBeginNode mergeEnds() {
         assert isDuplicate();
         List<EndNode> endsToMerge = new LinkedList<>();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentWhole.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentWhole.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,9 +22,8 @@
  */
 package org.graalvm.compiler.loop;
 
-import java.util.Collections;
-
 import org.graalvm.compiler.core.common.cfg.Loop;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
 import org.graalvm.compiler.graph.Node;
@@ -32,9 +31,11 @@
 import org.graalvm.compiler.nodes.EndNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
-import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
+import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.cfg.Block;
+import org.graalvm.util.EconomicSet;
 
 public class LoopFragmentWhole extends LoopFragment {
 
@@ -65,11 +66,7 @@
     public NodeBitMap nodes() {
         if (nodes == null) {
             Loop<Block> loop = loop().loop();
-            if (loop.getHeader().getBeginNode().graph().getGuardsStage() == GuardsStage.AFTER_FSA) {
-                nodes = LoopFragment.computeNodes(graph(), LoopFragment.toHirBlocks(loop.getBlocks()), Collections.emptyList());
-            } else {
-                nodes = LoopFragment.computeNodes(graph(), LoopFragment.toHirBlocks(loop.getBlocks()), LoopFragment.toHirExits(loop.getExits()));
-            }
+            nodes = LoopFragment.computeNodes(graph(), LoopFragment.toHirBlocks(loop.getBlocks()), LoopFragment.toHirExits(loop.getExits()));
         }
         return nodes;
     }
@@ -113,6 +110,44 @@
         // TODO (gd) ?
     }
 
+    void cleanupLoopExits() {
+        LoopBeginNode loopBegin = original().loop().loopBegin();
+        assert nodes == null || nodes.contains(loopBegin);
+        StructuredGraph graph = loopBegin.graph();
+        if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            // After FrameStateAssignment ControlFlowGraph treats loop exits differently which means
+            // that the LoopExitNodes can be in a block which post dominates the true loop exit. For
+            // cloning to work right they must agree.
+            EconomicSet<LoopExitNode> exits = EconomicSet.create();
+            for (Block exitBlock : original().loop().loop().getExits()) {
+                LoopExitNode exitNode = exitBlock.getLoopExit();
+                if (exitNode == null) {
+                    exitNode = graph.add(new LoopExitNode(loopBegin));
+                    graph.addAfterFixed(exitBlock.getBeginNode(), exitNode);
+                    if (nodes != null) {
+                        nodes.mark(exitNode);
+                    }
+                    graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "Adjusting loop exit node for %s", loopBegin);
+                }
+                exits.add(exitNode);
+            }
+            for (LoopExitNode exitNode : loopBegin.loopExits()) {
+                if (!exits.contains(exitNode)) {
+                    if (nodes != null) {
+                        nodes.clear(exitNode);
+                    }
+                    graph.removeFixed(exitNode);
+                }
+            }
+        }
+
+    }
+
+    @Override
+    protected void beforeDuplication() {
+        cleanupLoopExits();
+    }
+
     @Override
     public void insertBefore(LoopEx loop) {
         // TODO Auto-generated method stub
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopPolicies.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopPolicies.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,6 +26,7 @@
 
 import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+
 import jdk.vm.ci.meta.MetaAccessProvider;
 
 public interface LoopPolicies {
@@ -33,6 +34,8 @@
 
     boolean shouldFullUnroll(LoopEx loop);
 
+    boolean shouldPartiallyUnroll(LoopEx loop);
+
     boolean shouldTryUnswitch(LoopEx loop);
 
     boolean shouldUnswitch(LoopEx loop, List<ControlSplitNode> controlSplits);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopsData.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopsData.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,16 +28,15 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
-import org.graalvm.util.Equivalence;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.EconomicSet;
+import org.graalvm.util.Equivalence;
 
 public class LoopsData {
     private final EconomicMap<LoopBeginNode, LoopEx> loopBeginToEx = EconomicMap.create(Equivalence.IDENTITY);
@@ -46,10 +45,11 @@
 
     @SuppressWarnings("try")
     public LoopsData(final StructuredGraph graph) {
-        try (Scope s = Debug.scope("ControlFlowGraph")) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("ControlFlowGraph")) {
             cfg = ControlFlowGraph.compute(graph, true, true, true, true);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
         assert checkLoopOrder(cfg.getLoops());
         loops = new ArrayList<>(cfg.getLoops().size());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/GraphCopyBenchmark.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/GraphCopyBenchmark.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,7 +32,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 
 /**
- * Benchmarks the performance of {@link Graph#copy()}.
+ * Benchmarks the performance of {@link Graph#copy(org.graalvm.compiler.debug.DebugContext)}.
  */
 public class GraphCopyBenchmark extends GraalBenchmark {
 
@@ -75,8 +75,8 @@
 
     @Benchmark
     @Warmup(iterations = 20)
-    public StructuredGraph nullness(Nullness s, @SuppressWarnings("unused") GraalState g) {
-        return (StructuredGraph) s.graph.copy();
+    public StructuredGraph nullness(Nullness s, GraalState g) {
+        return (StructuredGraph) s.graph.copy(g.debug);
     }
 
     @MethodSpec(declaringClass = GraphCopyBenchmark.class, name = "searchSnippet")
@@ -127,7 +127,7 @@
 
     @Benchmark
     @Warmup(iterations = 20)
-    public StructuredGraph search(Search s, @SuppressWarnings("unused") GraalState g) {
-        return (StructuredGraph) s.graph.copy();
+    public StructuredGraph search(Search s, GraalState g) {
+        return (StructuredGraph) s.graph.copy(g.debug);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,30 +22,34 @@
  */
 package org.graalvm.compiler.microbenchmarks.graal.util;
 
-import jdk.vm.ci.meta.MetaAccessProvider;
-
+import org.graalvm.compiler.api.test.Graal;
+import org.graalvm.compiler.core.target.Backend;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.util.Providers;
+import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.State;
 
-import org.graalvm.compiler.api.test.Graal;
-import org.graalvm.compiler.core.target.Backend;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.util.Providers;
-import org.graalvm.compiler.runtime.RuntimeProvider;
+import jdk.vm.ci.meta.MetaAccessProvider;
 
 /**
- * Read-only, benchmark-wide state providing Graal runtime context.
+ * Read-only, thread-local state providing Graal runtime context. This has to be thread-local due to
+ * requirements that {@link DebugContext} objects be single threaded in their usage.
  */
-@State(Scope.Benchmark)
+@State(Scope.Thread)
 public class GraalState {
 
     public final OptionValues options;
+    public final DebugContext debug;
     public final Backend backend;
     public final Providers providers;
     public final MetaAccessProvider metaAccess;
 
     public GraalState() {
         options = Graal.getRequiredCapability(OptionValues.class);
+        debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
         backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
         providers = backend.getProviders();
         metaAccess = providers.getMetaAccess();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraalUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,6 @@
 import java.lang.reflect.Method;
 import java.util.List;
 
-import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.java.GraphBuilderPhase;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -33,7 +32,6 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
-import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
@@ -133,7 +131,8 @@
     }
 
     public static StructuredGraph getGraph(GraalState graal, ResolvedJavaMethod javaMethod, boolean useProfilingInfo) {
-        StructuredGraph graph = new StructuredGraph.Builder(Graal.getRequiredCapability(OptionValues.class), AllowAssumptions.YES).useProfilingInfo(useProfilingInfo).method(javaMethod).build();
+        StructuredGraph graph = new StructuredGraph.Builder(graal.options, graal.debug, AllowAssumptions.YES).useProfilingInfo(
+                        useProfilingInfo).method(javaMethod).build();
         PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>();
         graphBuilderSuite.appendPhase(new GraphBuilderPhase(GraphBuilderConfiguration.getDefault(new Plugins(new InvocationPlugins()))));
         graphBuilderSuite.apply(graph, new HighTierContext(graal.providers, graphBuilderSuite, OptimisticOptimizations.ALL));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraphState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/util/GraphState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,8 +25,7 @@
 import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getGraph;
 import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getMethodFromMethodSpec;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugEnvironment;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.openjdk.jmh.annotations.Level;
 import org.openjdk.jmh.annotations.Scope;
@@ -46,15 +45,13 @@
     @SuppressWarnings("try")
     public GraphState() {
         GraalState graal = new GraalState();
-        // Ensure a debug configuration for this thread is initialized
-        DebugEnvironment.ensureInitialized(graal.options);
-
+        DebugContext debug = graal.debug;
         ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethodFromMethodSpec(getClass()));
         StructuredGraph structuredGraph = null;
-        try (Debug.Scope s = Debug.scope("GraphState", method)) {
+        try (DebugContext.Scope s = debug.scope("GraphState", method)) {
             structuredGraph = preprocessOriginal(getGraph(graal, method));
         } catch (Throwable t) {
-            Debug.handle(t);
+            debug.handle(t);
         }
         this.originalGraph = structuredGraph;
     }
@@ -75,6 +72,6 @@
 
     @Setup(Level.Invocation)
     public void beforeInvocation() {
-        graph = (StructuredGraph) originalGraph.copy();
+        graph = (StructuredGraph) originalGraph.copy(originalGraph.getDebug());
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,12 +34,6 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import org.openjdk.jmh.annotations.Level;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.code.CompilationResult;
@@ -51,9 +45,8 @@
 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.target.Backend;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugEnvironment;
-import org.graalvm.compiler.debug.internal.DebugScope;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.lir.LIR;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
 import org.graalvm.compiler.lir.framemap.FrameMapBuilder;
@@ -81,6 +74,11 @@
 import org.graalvm.compiler.phases.tiers.TargetProvider;
 import org.graalvm.compiler.phases.util.Providers;
 import org.graalvm.compiler.runtime.RuntimeProvider;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
 
 import jdk.vm.ci.code.CodeCacheProvider;
 import jdk.vm.ci.code.RegisterConfig;
@@ -106,6 +104,7 @@
      * The graph processed by the benchmark.
      */
     private final OptionValues options;
+    private final DebugContext debug;
     private StructuredGraph graph;
     private final Backend backend;
     private final Providers providers;
@@ -119,12 +118,7 @@
         this.options = Graal.getRequiredCapability(OptionValues.class);
         this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
         this.providers = backend.getProviders();
-
-        // Ensure a debug configuration for this thread is initialized
-        if (Debug.isEnabled() && DebugScope.getConfig() == null) {
-            DebugEnvironment.ensureInitialized(options);
-        }
-
+        this.debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
     }
 
     protected boolean useProfilingInfo() {
@@ -136,10 +130,10 @@
         GraalState graal = new GraalState();
         ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethod());
         StructuredGraph structuredGraph = null;
-        try (Debug.Scope s = Debug.scope("GraphState", method)) {
+        try (DebugContext.Scope s = debug.scope("GraphState", method)) {
             structuredGraph = preprocessOriginal(getGraph(graal, method, useProfilingInfo()));
         } catch (Throwable t) {
-            Debug.handle(t);
+            debug.handle(t);
         }
         this.originalGraph = structuredGraph;
     }
@@ -321,7 +315,7 @@
     protected final void prepareRequest() {
         assert originalGraph != null : "call initialzeMethod first";
         CompilationIdentifier compilationId = backend.getCompilationIdentifier(originalGraph.method());
-        graph = originalGraph.copyWithIdentifier(compilationId);
+        graph = originalGraph.copyWithIdentifier(compilationId, originalGraph.getDebug());
         assert !graph.isFrozen();
         ResolvedJavaMethod installedCodeOwner = graph.method();
         request = new Request<>(graph, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL,
@@ -376,7 +370,7 @@
         codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
         linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 
-        LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder, getGraphOptions());
+        LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder, getGraphOptions(), getGraphDebug());
         FrameMapBuilder frameMapBuilder = request.backend.newFrameMapBuilder(registerConfig);
         lirGenRes = request.backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, request.graph, stub);
         lirGenTool = request.backend.newLIRGenerator(lirGenRes);
@@ -387,6 +381,10 @@
         return graph.getOptions();
     }
 
+    protected DebugContext getGraphDebug() {
+        return graph.getDebug();
+    }
+
     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/TraceBuilderBenchmark.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/trace/TraceBuilderBenchmark.java	Fri Jul 07 09:40:47 2017 -0700
@@ -44,12 +44,12 @@
 
     @Benchmark
     public TraceBuilderResult uniDirectionalTraceBuilder(State s) {
-        return UniDirectionalTraceBuilder.computeTraces(s.cfg.getStartBlock(), s.cfg.getBlocks(), TraceBuilderPhase.getTrivialTracePredicate(s.getLIR()));
+        return UniDirectionalTraceBuilder.computeTraces(s.getLIR().getDebug(), s.cfg.getStartBlock(), s.cfg.getBlocks(), TraceBuilderPhase.getTrivialTracePredicate(s.getLIR()));
     }
 
     @Benchmark
     public TraceBuilderResult biDirectionalTraceBuilder(State s) {
-        return BiDirectionalTraceBuilder.computeTraces(s.cfg.getStartBlock(), s.cfg.getBlocks(), TraceBuilderPhase.getTrivialTracePredicate(s.getLIR()));
+        return BiDirectionalTraceBuilder.computeTraces(s.getLIR().getDebug(), s.cfg.getStartBlock(), s.cfg.getBlocks(), TraceBuilderPhase.getTrivialTracePredicate(s.getLIR()));
     }
 
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -36,14 +36,17 @@
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.graph.test.GraphTest;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.options.OptionValues;
 
 /**
  * This class tests that integer stamps are created correctly for constants.
  */
-public class IntegerStampTest {
+public class IntegerStampTest extends GraphTest {
 
     private StructuredGraph graph;
 
@@ -53,7 +56,9 @@
 
     @Before
     public void before() {
-        graph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).build();
+        OptionValues options = getInitialOptions();
+        DebugContext debug = getDebug(options);
+        graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).build();
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,13 +22,16 @@
  */
 package org.graalvm.compiler.nodes.test;
 
+import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
 import static org.junit.Assert.assertEquals;
 
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
-import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.options.OptionValues;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -43,7 +46,9 @@
 
     @Before
     public void before() {
-        graph = new StructuredGraph.Builder(GraalCompilerTest.getInitialOptions(), AllowAssumptions.YES).build();
+        OptionValues options = getInitialOptions();
+        DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
+        graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).build();
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/StaticFieldAccessTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/StaticFieldAccessTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -40,7 +40,7 @@
             if (c.length != 1) {
                 throw new InternalError("can't find single constructor");
             }
-            tester.parseDebug(tester.asResolvedJavaMethod(c[0]), AllowAssumptions.YES);
+            tester.parse(tester.builder(tester.asResolvedJavaMethod(c[0]), AllowAssumptions.YES), tester.getDebugGraphBuilderSuite());
         }
 
         public Inner(Object o) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractEndNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractEndNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,6 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
 
-import java.util.Arrays;
 import java.util.Collections;
 
 import org.graalvm.compiler.core.common.type.StampFactory;
@@ -63,7 +62,7 @@
     public Iterable<? extends Node> cfgSuccessors() {
         AbstractMergeNode merge = merge();
         if (merge != null) {
-            return Arrays.asList(merge);
+            return Collections.singletonList(merge);
         }
         return Collections.emptyList();
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractMergeNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/AbstractMergeNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,7 +28,6 @@
 
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.IterableNodeType;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -183,7 +182,7 @@
                     }
                 }
             }
-            Debug.log("Split %s into ends for %s.", this, merge);
+            getDebug().log("Split %s into ends for %s.", this, merge);
             int numEnds = this.forwardEndCount();
             for (int i = 0; i < numEnds - 1; i++) {
                 AbstractEndNode end = forwardEndAt(numEnds - 1 - i);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/DeoptimizeNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/DeoptimizeNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.nodes;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -83,8 +83,11 @@
     @SuppressWarnings("deprecation")
     public int getDebugId() {
         int deoptDebugId = debugId;
-        if (deoptDebugId == DEFAULT_DEBUG_ID && (Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod())) {
-            deoptDebugId = this.getId();
+        if (deoptDebugId == DEFAULT_DEBUG_ID) {
+            DebugContext debug = getDebug();
+            if ((debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod())) {
+                deoptDebugId = this.getId();
+            }
         }
         return deoptDebugId;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -41,8 +41,6 @@
 import org.graalvm.compiler.bytecode.Bytecode;
 import org.graalvm.compiler.bytecode.Bytecodes;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.IterableNodeType;
 import org.graalvm.compiler.graph.NodeClass;
@@ -72,8 +70,6 @@
 public final class FrameState extends VirtualState implements IterableNodeType {
     public static final NodeClass<FrameState> TYPE = NodeClass.create(FrameState.class);
 
-    private static final DebugCounter FRAMESTATES_COUNTER = Debug.counter("FrameStateCount");
-
     /**
      * Marker value for the second slot of values that occupy two local variable or expression stack
      * slots. The marker value is used by the bytecode parser, but replaced with {@code null} in the
@@ -157,7 +153,6 @@
         this.duringCall = duringCall;
         assert !this.rethrowException || this.stackSize == 1 : "must have exception on top of the stack";
         assert this.locksSize() == this.monitorIdCount();
-        FRAMESTATES_COUNTER.increment();
     }
 
     public FrameState(FrameState outerFrameState, Bytecode code, int bci, List<ValueNode> values, int localsSize, int stackSize, boolean rethrowException, boolean duringCall,
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -41,7 +41,7 @@
 import org.graalvm.compiler.core.common.PermanentBailoutException;
 import org.graalvm.compiler.core.common.util.TypeReader;
 import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Edges;
 import org.graalvm.compiler.graph.Graph;
@@ -61,6 +61,7 @@
 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.compiler.options.OptionValues;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
@@ -341,23 +342,28 @@
     protected final Architecture architecture;
     /** The target graph where decoded nodes are added to. */
     protected final StructuredGraph graph;
+    protected final OptionValues options;
+    protected final DebugContext debug;
+
     private final EconomicMap<NodeClass<?>, ArrayDeque<Node>> reusableFloatingNodes;
 
     public GraphDecoder(Architecture architecture, StructuredGraph graph) {
         this.architecture = architecture;
         this.graph = graph;
+        this.options = graph.getOptions();
+        this.debug = graph.getDebug();
         reusableFloatingNodes = EconomicMap.create(Equivalence.IDENTITY);
     }
 
     @SuppressWarnings("try")
     public final void decode(EncodedGraph encodedGraph) {
-        try (Debug.Scope scope = Debug.scope("GraphDecoder", graph)) {
+        try (DebugContext.Scope scope = debug.scope("GraphDecoder", graph)) {
             MethodScope methodScope = new MethodScope(null, graph, encodedGraph, LoopExplosionKind.NONE);
             decode(createInitialLoopScope(methodScope, null));
             cleanupGraph(methodScope);
             assert graph.verify();
         } catch (Throwable ex) {
-            Debug.handle(ex);
+            debug.handle(ex);
         }
     }
 
@@ -1355,7 +1361,8 @@
 
     @Override
     public void run() {
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "Before loop detection");
+        DebugContext debug = graph.getDebug();
+        debug.dump(DebugContext.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";
@@ -1378,11 +1385,11 @@
             } else {
                 insertLoopNodes(loop);
             }
-            Debug.dump(Debug.DETAILED_LEVEL, graph, "After handling of loop %s", loop.header);
+            debug.dump(DebugContext.DETAILED_LEVEL, graph, "After handling of loop %s", loop.header);
         }
 
         logIrreducibleLoops();
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "After loop detection");
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "After loop detection");
     }
 
     private List<Loop> findLoops() {
@@ -1909,15 +1916,16 @@
      */
     @SuppressWarnings("try")
     private void logIrreducibleLoops() {
-        try (Debug.Scope s = Debug.scope("IrreducibleLoops")) {
-            if (Debug.isLogEnabled(Debug.BASIC_LEVEL) && irreducibleLoopSwitch != null) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("IrreducibleLoops")) {
+            if (debug.isLogEnabled(DebugContext.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_LEVEL, "%s", msg);
+                debug.log(DebugContext.BASIC_LEVEL, "%s", msg);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,7 +33,7 @@
 import org.graalvm.compiler.core.common.util.TypeReader;
 import org.graalvm.compiler.core.common.util.TypeWriter;
 import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Edges;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -42,8 +42,8 @@
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
+import org.graalvm.util.Pair;
 import org.graalvm.util.UnmodifiableMapCursor;
-import org.graalvm.util.Pair;
 
 import jdk.vm.ci.code.Architecture;
 
@@ -418,7 +418,8 @@
      */
     @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();
+        DebugContext debug = originalGraph.getDebug();
+        StructuredGraph decodedGraph = new StructuredGraph.Builder(originalGraph.getOptions(), debug, AllowAssumptions.YES).method(originalGraph.method()).build();
         GraphDecoder decoder = new GraphDecoder(architecture, decodedGraph);
         decoder.decode(encodedGraph);
 
@@ -426,9 +427,10 @@
         try {
             GraphComparison.verifyGraphsEqual(originalGraph, decodedGraph);
         } catch (Throwable ex) {
-            try (Debug.Scope scope = Debug.scope("GraphEncoder")) {
-                Debug.dump(Debug.VERBOSE_LEVEL, originalGraph, "Original Graph");
-                Debug.dump(Debug.VERBOSE_LEVEL, decodedGraph, "Decoded Graph");
+            originalGraph.getDebug();
+            try (DebugContext.Scope scope = debug.scope("GraphEncoder")) {
+                debug.dump(DebugContext.VERBOSE_LEVEL, originalGraph, "Original Graph");
+                debug.dump(DebugContext.VERBOSE_LEVEL, decodedGraph, "Decoded Graph");
             }
             throw ex;
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,8 +34,8 @@
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -73,7 +73,7 @@
 public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable {
     public static final NodeClass<IfNode> TYPE = NodeClass.create(IfNode.class);
 
-    private static final DebugCounter CORRECTED_PROBABILITIES = Debug.counter("CorrectedProbabilities");
+    private static final CounterKey CORRECTED_PROBABILITIES = DebugContext.counter("CorrectedProbabilities");
 
     @Successor AbstractBeginNode trueSuccessor;
     @Successor AbstractBeginNode falseSuccessor;
@@ -179,12 +179,12 @@
     public void simplify(SimplifierTool tool) {
         if (trueSuccessor().next() instanceof DeoptimizeNode) {
             if (trueSuccessorProbability != 0) {
-                CORRECTED_PROBABILITIES.increment();
+                CORRECTED_PROBABILITIES.increment(getDebug());
                 trueSuccessorProbability = 0;
             }
         } else if (falseSuccessor().next() instanceof DeoptimizeNode) {
             if (trueSuccessorProbability != 1) {
-                CORRECTED_PROBABILITIES.increment();
+                CORRECTED_PROBABILITIES.increment(getDebug());
                 trueSuccessorProbability = 1;
             }
         }
@@ -453,12 +453,13 @@
     }
 
     private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b) {
+        DebugContext debug = a.getDebug();
         if (a instanceof InstanceOfNode) {
             InstanceOfNode instanceOfA = (InstanceOfNode) a;
             if (b instanceof IsNullNode) {
                 IsNullNode isNullNode = (IsNullNode) b;
                 if (isNullNode.getValue() == instanceOfA.getValue()) {
-                    Debug.log("Can swap instanceof and isnull if");
+                    debug.log("Can swap instanceof and isnull if");
                     return true;
                 }
             } else if (b instanceof InstanceOfNode) {
@@ -466,7 +467,7 @@
                 if (instanceOfA.getValue() == instanceOfB.getValue() && !instanceOfA.type().getType().isInterface() && !instanceOfB.type().getType().isInterface() &&
                                 !instanceOfA.type().getType().isAssignableFrom(instanceOfB.type().getType()) && !instanceOfB.type().getType().isAssignableFrom(instanceOfA.type().getType())) {
                     // Two instanceof on the same value with mutually exclusive types.
-                    Debug.log("Can swap instanceof for types %s and %s", instanceOfA.type(), instanceOfB.type());
+                    debug.log("Can swap instanceof for types %s and %s", instanceOfA.type(), instanceOfB.type());
                     return true;
                 }
             }
@@ -479,7 +480,7 @@
             if (b instanceof CompareNode) {
                 CompareNode compareB = (CompareNode) b;
                 if (compareA == compareB) {
-                    Debug.log("Same conditions => do not swap and leave the work for global value numbering.");
+                    debug.log("Same conditions => do not swap and leave the work for global value numbering.");
                     return false;
                 }
                 if (compareB.unorderedIsTrue()) {
@@ -497,7 +498,7 @@
                     Condition combined = conditionA.join(comparableCondition);
                     if (combined == null) {
                         // The two conditions are disjoint => can reorder.
-                        Debug.log("Can swap disjoint coditions on same values: %s and %s", conditionA, comparableCondition);
+                        debug.log("Can swap disjoint coditions on same values: %s and %s", conditionA, comparableCondition);
                         return true;
                     }
                 } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) {
@@ -513,7 +514,7 @@
                     }
 
                     if (canSwap) {
-                        Debug.log("Can swap equality condition with one shared and one disjoint value.");
+                        debug.log("Can swap equality condition with one shared and one disjoint value.");
                         return true;
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -43,9 +43,20 @@
 
     public static final NodeClass<LoopBeginNode> TYPE = NodeClass.create(LoopBeginNode.class);
     protected double loopFrequency;
+    protected double loopOrigFrequency;
     protected int nextEndIndex;
     protected int unswitches;
+    protected int splits;
     protected int inversionCount;
+    protected LoopType loopType;
+    protected int unrollFactor;
+
+    public enum LoopType {
+        SIMPLE_LOOP,
+        PRE_LOOP,
+        MAIN_LOOP,
+        POST_LOOP
+    }
 
     /** See {@link LoopEndNode#canSafepoint} for more information. */
     boolean canEndsSafepoint;
@@ -55,7 +66,51 @@
     public LoopBeginNode() {
         super(TYPE);
         loopFrequency = 1;
+        loopOrigFrequency = 1;
+        unswitches = 0;
+        splits = 0;
         this.canEndsSafepoint = true;
+        loopType = LoopType.SIMPLE_LOOP;
+        unrollFactor = 1;
+    }
+
+    public boolean isSimpleLoop() {
+        return (loopType == LoopType.SIMPLE_LOOP);
+    }
+
+    public void setPreLoop() {
+        assert isSimpleLoop();
+        loopType = LoopType.PRE_LOOP;
+    }
+
+    public boolean isPreLoop() {
+        return (loopType == LoopType.PRE_LOOP);
+    }
+
+    public void setMainLoop() {
+        assert isSimpleLoop();
+        loopType = LoopType.MAIN_LOOP;
+    }
+
+    public boolean isMainLoop() {
+        return (loopType == LoopType.MAIN_LOOP);
+    }
+
+    public void setPostLoop() {
+        assert isSimpleLoop();
+        loopType = LoopType.POST_LOOP;
+    }
+
+    public boolean isPostLoop() {
+        return (loopType == LoopType.POST_LOOP);
+    }
+
+    public int getUnrollFactor() {
+        return unrollFactor;
+    }
+
+    public void setUnrollFactor(int currentUnrollFactor) {
+        unrollFactor = currentUnrollFactor;
     }
 
     /** Disables safepoint for the whole loop, i.e., for all {@link LoopEndNode loop ends}. */
@@ -68,6 +123,15 @@
         }
     }
 
+    public double loopOrigFrequency() {
+        return loopOrigFrequency;
+    }
+
+    public void setLoopOrigFrequency(double loopOrigFrequency) {
+        assert loopOrigFrequency >= 0;
+        this.loopOrigFrequency = loopOrigFrequency;
+    }
+
     public double loopFrequency() {
         return loopFrequency;
     }
@@ -122,11 +186,23 @@
         return result;
     }
 
+    public boolean isSingleEntryLoop() {
+        return (forwardEndCount() == 1);
+    }
+
     public AbstractEndNode forwardEnd() {
         assert forwardEndCount() == 1;
         return forwardEndAt(0);
     }
 
+    public int splits() {
+        return splits;
+    }
+
+    public void incrementSplits() {
+        splits++;
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool gen) {
         // Nothing to emit, since this is node is used for structural purposes only.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,6 +33,7 @@
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.JavaMethodContext;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
@@ -167,60 +168,95 @@
         private boolean useProfilingInfo = true;
         private final OptionValues options;
         private Cancellable cancellable = null;
+        private final DebugContext debug;
 
         /**
          * Creates a builder for a graph.
          */
-        public Builder(OptionValues options, AllowAssumptions allowAssumptions) {
+        public Builder(OptionValues options, DebugContext debug, AllowAssumptions allowAssumptions) {
             this.options = options;
+            this.debug = debug;
             this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null;
         }
 
         /**
          * Creates a builder for a graph that does not support {@link Assumptions}.
          */
-        public Builder(OptionValues options) {
+        public Builder(OptionValues options, DebugContext debug) {
             this.options = options;
+            this.debug = debug;
             assumptions = null;
         }
 
+        public String getName() {
+            return name;
+        }
+
         public Builder name(String s) {
             this.name = s;
             return this;
         }
 
+        public ResolvedJavaMethod getMethod() {
+            return rootMethod;
+        }
+
         public Builder method(ResolvedJavaMethod method) {
             this.rootMethod = method;
             return this;
         }
 
+        public DebugContext getDebug() {
+            return debug;
+        }
+
+        public SpeculationLog getSpeculationLog() {
+            return speculationLog;
+        }
+
         public Builder speculationLog(SpeculationLog log) {
             this.speculationLog = log;
             return this;
         }
 
+        public CompilationIdentifier getCompilationId() {
+            return compilationId;
+        }
+
         public Builder compilationId(CompilationIdentifier id) {
             this.compilationId = id;
             return this;
         }
 
+        public Cancellable getCancellable() {
+            return cancellable;
+        }
+
         public Builder cancellable(Cancellable cancel) {
             this.cancellable = cancel;
             return this;
         }
 
+        public int getEntryBCI() {
+            return entryBCI;
+        }
+
         public Builder entryBCI(int bci) {
             this.entryBCI = bci;
             return this;
         }
 
+        public boolean getUseProfilingInfo() {
+            return useProfilingInfo;
+        }
+
         public Builder useProfilingInfo(boolean flag) {
             this.useProfilingInfo = flag;
             return this;
         }
 
         public StructuredGraph build() {
-            return new StructuredGraph(name, rootMethod, entryBCI, assumptions, speculationLog, useProfilingInfo, compilationId, options, cancellable);
+            return new StructuredGraph(name, rootMethod, entryBCI, assumptions, speculationLog, useProfilingInfo, compilationId, options, debug, cancellable);
         }
     }
 
@@ -280,8 +316,9 @@
                     boolean useProfilingInfo,
                     CompilationIdentifier compilationId,
                     OptionValues options,
+                    DebugContext debug,
                     Cancellable cancellable) {
-        super(name, options);
+        super(name, options, debug);
         this.setStart(add(new StartNode()));
         this.rootMethod = method;
         this.graphId = uniqueGraphIds.incrementAndGet();
@@ -404,13 +441,16 @@
      *
      * @param newName the name of the copy, used for debugging purposes (can be null)
      * @param duplicationMapCallback consumer of the duplication map created during the copying
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
      */
     @Override
-    protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback) {
-        return copy(newName, duplicationMapCallback, compilationId);
+    protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) {
+        return copy(newName, duplicationMapCallback, compilationId, debugForCopy);
     }
 
-    private StructuredGraph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId) {
+    private StructuredGraph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId, DebugContext debugForCopy) {
         AllowAssumptions allowAssumptions = AllowAssumptions.ifNonNull(assumptions);
         StructuredGraph copy = new StructuredGraph(newName,
                         method(),
@@ -419,7 +459,7 @@
                         speculationLog,
                         useProfilingInfo,
                         newCompilationId,
-                        getOptions(), null);
+                        getOptions(), debugForCopy, null);
         if (allowAssumptions == AllowAssumptions.YES && assumptions != null) {
             copy.assumptions.record(assumptions);
         }
@@ -437,8 +477,13 @@
         return copy;
     }
 
-    public StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId) {
-        return copy(name, null, newCompilationId);
+    /**
+     * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
+     *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
+     *            accessed by multiple threads).
+     */
+    public StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId, DebugContext debugForCopy) {
+        return copy(name, null, newCompilationId, debugForCopy);
     }
 
     public ParameterNode getParameter(int index) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/Block.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/Block.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,11 +31,13 @@
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodeinfo.Verbosity;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.BeginNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
+import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
 import org.graalvm.word.LocationIdentity;
 
@@ -48,7 +50,7 @@
     protected FixedNode endNode;
 
     protected double probability;
-    protected Loop<Block> loop;
+    private Loop<Block> loop;
 
     protected Block postdominator;
     protected Block distancedDominatorCache;
@@ -67,6 +69,21 @@
         return endNode;
     }
 
+    /**
+     * Return the {@link LoopExitNode} for this block if it exists.
+     */
+    public LoopExitNode getLoopExit() {
+        if (beginNode instanceof BeginNode) {
+            if (beginNode.next() instanceof LoopExitNode) {
+                return (LoopExitNode) beginNode.next();
+            }
+        }
+        if (beginNode instanceof LoopExitNode) {
+            return (LoopExitNode) beginNode;
+        }
+        return null;
+    }
+
     @Override
     public Loop<Block> getLoop() {
         return loop;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,7 +30,7 @@
 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
 import org.graalvm.compiler.core.common.cfg.CFGVerifier;
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
@@ -594,7 +594,7 @@
                 if (beginNode instanceof LoopBeginNode) {
                     Loop<Block> loop = new HIRLoop(block.getLoop(), loops.size(), block);
                     loops.add(loop);
-                    block.loop = loop;
+                    block.setLoop(loop);
                     loop.getBlocks().add(block);
 
                     LoopBeginNode loopBegin = (LoopBeginNode) beginNode;
@@ -608,7 +608,7 @@
                             Block exitBlock = nodeToBlock.get(exit);
                             assert exitBlock.getPredecessorCount() == 1;
                             computeLoopBlocks(exitBlock.getFirstPredecessor(), loop, stack, true);
-                            loop.getExits().add(exitBlock);
+                            loop.addExit(exitBlock);
                         }
 
                         // The following loop can add new blocks to the end of the loop's block
@@ -617,10 +617,10 @@
                         for (int i = 0; i < size; ++i) {
                             Block b = loop.getBlocks().get(i);
                             for (Block sux : b.getSuccessors()) {
-                                if (sux.loop != loop) {
+                                if (sux.getLoop() != loop) {
                                     AbstractBeginNode begin = sux.getBeginNode();
                                     if (!(begin instanceof LoopExitNode && ((LoopExitNode) begin).loopBegin() == loopBegin)) {
-                                        Debug.log(Debug.VERBOSE_LEVEL, "Unexpected loop exit with %s, including whole branch in the loop", sux);
+                                        graph.getDebug().log(DebugContext.VERBOSE_LEVEL, "Unexpected loop exit with %s, including whole branch in the loop", sux);
                                         computeLoopBlocks(sux, loop, stack, false);
                                     }
                                 }
@@ -645,7 +645,7 @@
                                 // we might exit multiple loops if b.loops is not a loop at depth 0
                                 Loop<Block> curr = b.getLoop();
                                 while (curr != null) {
-                                    curr.getExits().add(succ);
+                                    curr.addExit(succ);
                                     curr = curr.getParent();
                                 }
                             } else {
@@ -667,7 +667,7 @@
                                     assert !Loop.transitiveParentLoop(succ.getLoop(), b.getLoop());
                                     Loop<Block> curr = b.getLoop();
                                     while (curr != null && curr != succ.getLoop()) {
-                                        curr.getExits().add(succ);
+                                        curr.addExit(succ);
                                         curr = curr.getParent();
                                     }
                                 }
@@ -681,8 +681,8 @@
     }
 
     private static void computeLoopBlocks(Block start, Loop<Block> loop, Block[] stack, boolean usePred) {
-        if (start.loop != loop) {
-            start.loop = loop;
+        if (start.getLoop() != loop) {
+            start.setLoop(loop);
             stack[0] = start;
             loop.getBlocks().add(start);
             int tos = 0;
@@ -691,9 +691,9 @@
 
                 // Add predecessors or successors to the loop.
                 for (Block b : (usePred ? block.getPredecessors() : block.getSuccessors())) {
-                    if (b.loop != loop) {
+                    if (b.getLoop() != loop) {
                         stack[++tos] = b;
-                        b.loop = loop;
+                        b.setLoop(loop);
                         loop.getBlocks().add(b);
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,6 +23,7 @@
 package org.graalvm.compiler.nodes.graphbuilderconf;
 
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.StampProvider;
@@ -66,6 +67,10 @@
         return getGraph().getOptions();
     }
 
+    default DebugContext getDebug() {
+        return getGraph().getDebug();
+    }
+
     /**
      * Determines if this parsing context is within the bytecode of an intrinsic or a method inlined
      * by an intrinsic.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -88,7 +88,18 @@
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        return ReadNode.canonicalizeRead(this, getAddress(), getLocationIdentity(), tool);
+        Node result = ReadNode.canonicalizeRead(this, getAddress(), getLocationIdentity(), tool);
+        if (result != this) {
+            return result;
+        }
+        if (tool.canonicalizeReads() && getAddress().hasMoreThanOneUsage() && lastLocationAccess instanceof WriteNode) {
+            WriteNode write = (WriteNode) lastLocationAccess;
+            if (write.getAddress() == getAddress() && write.getAccessStamp().isCompatible(getAccessStamp())) {
+                // Same memory location with no intervening write
+                return write.value();
+            }
+        }
+        return this;
     }
 
     @SuppressWarnings("try")
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,7 +24,10 @@
 
 import org.graalvm.compiler.core.common.LIRKind;
 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;
+import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
@@ -35,7 +38,7 @@
  * Writes a given {@linkplain #value() value} a {@linkplain FixedAccessNode memory location}.
  */
 @NodeInfo(nameTemplate = "Write#{p#location/s}")
-public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess {
+public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess, Canonicalizable {
 
     public static final NodeClass<WriteNode> TYPE = NodeClass.create(WriteNode.class);
 
@@ -62,4 +65,16 @@
     public Stamp getAccessStamp() {
         return value().stamp();
     }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (tool.canonicalizeReads() && hasExactlyOneUsage() && next() instanceof WriteNode) {
+            WriteNode write = (WriteNode) next();
+            if (write.lastLocationAccess == this && write.getAddress() == getAddress() && getAccessStamp().isCompatible(write.getAccessStamp())) {
+                write.setLastLocationAccess(getLastLocationAccess());
+                return write;
+            }
+        }
+        return this;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,6 +24,7 @@
 
 import java.util.List;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MonitorIdNode;
@@ -158,4 +159,6 @@
     boolean ensureMaterialized(VirtualObjectNode virtualObject);
 
     OptionValues getOptions();
+
+    DebugContext getDebug();
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,14 +25,16 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.BiFunction;
 
 import org.graalvm.compiler.bytecode.Bytecode;
 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.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
@@ -44,6 +46,7 @@
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
+import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
@@ -58,11 +61,16 @@
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
+import org.graalvm.compiler.nodes.java.MonitorIdNode;
 import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
 import org.graalvm.compiler.nodes.spi.LimitedValueProxy;
 import org.graalvm.compiler.nodes.spi.LoweringProvider;
 import org.graalvm.compiler.nodes.spi.ValueProxy;
+import org.graalvm.compiler.nodes.spi.VirtualizerTool;
+import org.graalvm.compiler.nodes.virtual.VirtualArrayNode;
+import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 import org.graalvm.compiler.options.Option;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
@@ -77,8 +85,10 @@
 import jdk.vm.ci.meta.Assumptions;
 import jdk.vm.ci.meta.Constant;
 import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
 
 public class GraphUtil {
 
@@ -98,7 +108,8 @@
 
         fixSurvivingAffectedMerges(markedNodes, unmarkedMerges);
 
-        Debug.dump(Debug.DETAILED_LEVEL, node.graph(), "After fixing merges (killCFG %s)", node);
+        DebugContext debug = node.getDebug();
+        debug.dump(DebugContext.DETAILED_LEVEL, node.graph(), "After fixing merges (killCFG %s)", node);
 
         // Mark non-fixed nodes
         markUsages(markedNodes);
@@ -112,7 +123,7 @@
                 }
             }
         }
-        Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "After disconnecting non-marked inputs (killCFG %s)", node);
+        debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "After disconnecting non-marked inputs (killCFG %s)", node);
         // Kill marked nodes
         for (Node marked : markedNodes) {
             if (marked.isAlive()) {
@@ -218,7 +229,8 @@
 
     @SuppressWarnings("try")
     public static void killCFG(FixedNode node) {
-        try (Debug.Scope scope = Debug.scope("KillCFG", node)) {
+        DebugContext debug = node.getDebug();
+        try (DebugContext.Scope scope = debug.scope("KillCFG", node)) {
             EconomicSet<Node> unusedNodes = null;
             EconomicSet<Node> unsafeNodes = null;
             Graph.NodeEventScope nodeEventScope = null;
@@ -237,9 +249,9 @@
                     }
                 });
             }
-            Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "Before killCFG %s", node);
+            debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "Before killCFG %s", node);
             killCFGInner(node);
-            Debug.dump(Debug.VERY_DETAILED_LEVEL, node.graph(), "After killCFG %s", node);
+            debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "After killCFG %s", node);
             if (Graph.Options.VerifyGraalGraphEdges.getValue(options)) {
                 EconomicSet<Node> newUnsafeNodes = collectUnsafeNodes(node.graph());
                 newUnsafeNodes.removeAll(unsafeNodes);
@@ -257,7 +269,7 @@
                 assert unusedNodes.isEmpty() : "New unused nodes: " + unusedNodes;
             }
         } catch (Throwable t) {
-            throw Debug.handle(t);
+            throw debug.handle(t);
         }
     }
 
@@ -935,4 +947,79 @@
         }
         return null;
     }
+
+    /**
+     * Virtualize an array copy.
+     *
+     * @param tool the virtualization tool
+     * @param source the source array
+     * @param sourceLength the length of the source array
+     * @param newLength the length of the new array
+     * @param from the start index in the source array
+     * @param newComponentType the component type of the new array
+     * @param elementKind the kind of the new array elements
+     * @param graph the node graph
+     * @param virtualArrayProvider a functional provider that returns a new virtual array given the
+     *            component type and length
+     */
+    public static void virtualizeArrayCopy(VirtualizerTool tool, ValueNode source, ValueNode sourceLength, ValueNode newLength, ValueNode from, ResolvedJavaType newComponentType, JavaKind elementKind,
+                    StructuredGraph graph, BiFunction<ResolvedJavaType, Integer, VirtualArrayNode> virtualArrayProvider) {
+
+        ValueNode sourceAlias = tool.getAlias(source);
+        ValueNode replacedSourceLength = tool.getAlias(sourceLength);
+        ValueNode replacedNewLength = tool.getAlias(newLength);
+        ValueNode replacedFrom = tool.getAlias(from);
+        if (!replacedNewLength.isConstant() || !replacedFrom.isConstant() || !replacedSourceLength.isConstant()) {
+            return;
+        }
+
+        assert newComponentType != null : "An array copy can be virtualized only if the real type of the resulting array is known statically.";
+
+        int fromInt = replacedFrom.asJavaConstant().asInt();
+        int newLengthInt = replacedNewLength.asJavaConstant().asInt();
+        int sourceLengthInt = replacedSourceLength.asJavaConstant().asInt();
+        if (sourceAlias instanceof VirtualObjectNode) {
+            VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias;
+            assert sourceLengthInt == sourceVirtual.entryCount();
+        }
+
+        if (fromInt < 0 || newLengthInt < 0 || fromInt > sourceLengthInt) {
+            /* Illegal values for either from index, the new length or the source length. */
+            return;
+        }
+
+        if (newLengthInt >= tool.getMaximumEntryCount()) {
+            /* The new array size is higher than maximum allowed size of virtualized objects. */
+            return;
+        }
+
+        ValueNode[] newEntryState = new ValueNode[newLengthInt];
+        int readLength = Math.min(newLengthInt, sourceLengthInt - fromInt);
+
+        if (sourceAlias instanceof VirtualObjectNode) {
+            /* The source array is virtualized, just copy over the values. */
+            VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias;
+            for (int i = 0; i < readLength; i++) {
+                newEntryState[i] = tool.getEntry(sourceVirtual, fromInt + i);
+            }
+        } else {
+            /* The source array is not virtualized, emit index loads. */
+            for (int i = 0; i < readLength; i++) {
+                LoadIndexedNode load = new LoadIndexedNode(null, sourceAlias, ConstantNode.forInt(i + fromInt, graph), elementKind);
+                tool.addNode(load);
+                newEntryState[i] = load;
+            }
+        }
+        if (readLength < newLengthInt) {
+            /* Pad the copy with the default value of its elment kind. */
+            ValueNode defaultValue = ConstantNode.defaultForKind(elementKind, graph);
+            for (int i = readLength; i < newLengthInt; i++) {
+                newEntryState[i] = defaultValue;
+            }
+        }
+        /* Perform the replacement. */
+        VirtualArrayNode newVirtualArray = virtualArrayProvider.apply(newComponentType, newLengthInt);
+        tool.createVirtualObject(newVirtualArray, newEntryState, Collections.<MonitorIdNode> emptyList(), false);
+        tool.replaceWithVirtual(newVirtualArray);
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/UniquePathUtilities.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/*
- * Copyright (c) 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.options;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-public class UniquePathUtilities {
-
-    private static final AtomicLong globalTimeStamp = new AtomicLong();
-    /**
-     * This generates a per thread persistent id to aid mapping related dump files with each other.
-     */
-    private static final ThreadLocal<PerThreadSequence> threadDumpId = new ThreadLocal<>();
-    private static final AtomicInteger dumpId = new AtomicInteger();
-
-    static class PerThreadSequence {
-        final int threadID;
-        HashMap<String, Integer> sequences = new HashMap<>(2);
-
-        PerThreadSequence(int threadID) {
-            this.threadID = threadID;
-        }
-
-        String generateID(String extension) {
-            Integer box = sequences.get(extension);
-            if (box == null) {
-                sequences.put(extension, 1);
-                return Integer.toString(threadID);
-            } else {
-                sequences.put(extension, box + 1);
-                return Integer.toString(threadID) + '-' + box;
-            }
-        }
-    }
-
-    private static String getThreadDumpId(String extension) {
-        PerThreadSequence id = threadDumpId.get();
-        if (id == null) {
-            id = new PerThreadSequence(dumpId.incrementAndGet());
-            threadDumpId.set(id);
-        }
-        return id.generateID(extension);
-    }
-
-    private static String formatExtension(String ext) {
-        if (ext == null || ext.length() == 0) {
-            return "";
-        }
-        return "." + ext;
-    }
-
-    public static long getGlobalTimeStamp() {
-        if (globalTimeStamp.get() == 0) {
-            globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
-        }
-        return globalTimeStamp.get();
-    }
-
-    /**
-     * Generates a {@link Path} using the format "%s-%d_%d%s" with the {@link OptionKey#getValue
-     * base filename}, a {@link #globalTimeStamp global timestamp} , {@link #getThreadDumpId a per
-     * thread unique id} and an optional {@code extension}.
-     *
-     * @return the output file path or null if the flag is null
-     */
-    public static Path getPath(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension) {
-        return getPath(options, option, defaultDirectory, extension, true);
-    }
-
-    /**
-     * Generate a {@link Path} using the format "%s-%d_%s" with the {@link OptionKey#getValue base
-     * filename}, a {@link #globalTimeStamp global timestamp} and an optional {@code extension} .
-     *
-     * @return the output file path or null if the flag is null
-     */
-    public static Path getPathGlobal(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension) {
-        return getPath(options, option, defaultDirectory, extension, false);
-    }
-
-    private static Path getPath(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension, boolean includeThreadId) {
-        if (option.getValue(options) == null) {
-            return null;
-        }
-        String ext = formatExtension(extension);
-        final String name = includeThreadId
-                        ? String.format("%s-%d_%s%s", option.getValue(options), getGlobalTimeStamp(), getThreadDumpId(ext), ext)
-                        : String.format("%s-%d%s", option.getValue(options), getGlobalTimeStamp(), ext);
-        Path result = Paths.get(name);
-        if (result.isAbsolute() || defaultDirectory == null) {
-            return result;
-        }
-        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/AddressLoweringByUsePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,8 +23,11 @@
  */
 package org.graalvm.compiler.phases.common;
 
+import jdk.vm.ci.meta.JavaKind;
 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.nodes.PrefetchAllocateNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.JavaReadNode;
@@ -81,13 +84,11 @@
                 Stamp stamp = abstractWriteNode.value().stamp();
                 address = abstractWriteNode.getAddress();
                 lowered = lowering.lower(abstractWriteNode, stamp, address);
-                // TODO -- PrefetchAllocateNode is not yet implemented for AArch64
-                // } else if (node instanceof PrefetchAllocateNode) {
-                // PrefetchAllocateNode prefetchAllocateNode = (PrefetchAllocateNode) node;
-                // Stamp stamp = prefetchAllocateNode.value().stamp();
-                // n.b.this getter is not provided!
-                // address = prefetchAllocateNode.getAddress();
-                // lowered = lowering.lower(prefetchAllocateNode, stamp, address);
+            } else if (node instanceof PrefetchAllocateNode) {
+                PrefetchAllocateNode prefetchAllocateNode = (PrefetchAllocateNode) node;
+                Stamp stamp = StampFactory.forKind(JavaKind.Object);
+                address = (AddressNode) prefetchAllocateNode.inputs().first();
+                lowered = lowering.lower(prefetchAllocateNode, stamp, address);
             } else {
                 continue;
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,9 +23,9 @@
 package org.graalvm.compiler.phases.common;
 
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Graph.Mark;
@@ -62,13 +62,13 @@
 public class CanonicalizerPhase extends BasePhase<PhaseContext> {
 
     private static final int MAX_ITERATION_PER_NODE = 10;
-    private static final DebugCounter COUNTER_CANONICALIZED_NODES = Debug.counter("CanonicalizedNodes");
-    private static final DebugCounter COUNTER_PROCESSED_NODES = Debug.counter("ProcessedNodes");
-    private static final DebugCounter COUNTER_CANONICALIZATION_CONSIDERED_NODES = Debug.counter("CanonicalizationConsideredNodes");
-    private static final DebugCounter COUNTER_INFER_STAMP_CALLED = Debug.counter("InferStampCalled");
-    private static final DebugCounter COUNTER_STAMP_CHANGED = Debug.counter("StampChanged");
-    private static final DebugCounter COUNTER_SIMPLIFICATION_CONSIDERED_NODES = Debug.counter("SimplificationConsideredNodes");
-    private static final DebugCounter COUNTER_GLOBAL_VALUE_NUMBERING_HITS = Debug.counter("GlobalValueNumberingHits");
+    private static final CounterKey COUNTER_CANONICALIZED_NODES = DebugContext.counter("CanonicalizedNodes");
+    private static final CounterKey COUNTER_PROCESSED_NODES = DebugContext.counter("ProcessedNodes");
+    private static final CounterKey COUNTER_CANONICALIZATION_CONSIDERED_NODES = DebugContext.counter("CanonicalizationConsideredNodes");
+    private static final CounterKey COUNTER_INFER_STAMP_CALLED = DebugContext.counter("InferStampCalled");
+    private static final CounterKey COUNTER_STAMP_CHANGED = DebugContext.counter("StampChanged");
+    private static final CounterKey COUNTER_SIMPLIFICATION_CONSIDERED_NODES = DebugContext.counter("SimplificationConsideredNodes");
+    private static final CounterKey COUNTER_GLOBAL_VALUE_NUMBERING_HITS = DebugContext.counter("GlobalValueNumberingHits");
 
     private boolean globalValueNumber = true;
     private boolean canonicalizeReads = true;
@@ -161,6 +161,7 @@
 
         private NodeWorkList workList;
         private Tool tool;
+        private DebugContext debug;
 
         private Instance(PhaseContext context) {
             this(context, null, null);
@@ -187,6 +188,7 @@
 
         @Override
         protected void run(StructuredGraph graph) {
+            this.debug = graph.getDebug();
             boolean wholeGraph = newNodesMark == null || newNodesMark.isStart();
             if (initWorkingSet == null) {
                 workList = graph.createIterativeNodeWorkList(wholeGraph, MAX_ITERATION_PER_NODE);
@@ -229,8 +231,8 @@
             try (NodeEventScope nes = graph.trackNodeEvents(listener)) {
                 for (Node n : workList) {
                     boolean changed = processNode(n);
-                    if (changed && Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
-                        Debug.dump(Debug.DETAILED_LEVEL, graph, "CanonicalizerPhase %s", n);
+                    if (changed && debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
+                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "CanonicalizerPhase %s", n);
                     }
                 }
             }
@@ -243,7 +245,7 @@
             if (!node.isAlive()) {
                 return false;
             }
-            COUNTER_PROCESSED_NODES.increment();
+            COUNTER_PROCESSED_NODES.increment(debug);
             if (GraphUtil.tryKillUnused(node)) {
                 return true;
             }
@@ -261,7 +263,7 @@
                 Constant constant = valueNode.stamp().asConstant();
                 if (constant != null && !(node instanceof ConstantNode)) {
                     ConstantNode stampConstant = ConstantNode.forConstant(valueNode.stamp(), constant, context.getMetaAccess(), graph);
-                    Debug.log("Canonicalizer: constant stamp replaces %1s with %1s", valueNode, stampConstant);
+                    debug.log("Canonicalizer: constant stamp replaces %1s with %1s", valueNode, stampConstant);
                     valueNode.replaceAtUsages(InputType.Value, stampConstant);
                     GraphUtil.tryKillUnused(valueNode);
                     return true;
@@ -282,8 +284,8 @@
                 if (newNode != null) {
                     assert !(node instanceof FixedNode || newNode instanceof FixedNode);
                     node.replaceAtUsagesAndDelete(newNode);
-                    COUNTER_GLOBAL_VALUE_NUMBERING_HITS.increment();
-                    Debug.log("GVN applied and new node is %1s", newNode);
+                    COUNTER_GLOBAL_VALUE_NUMBERING_HITS.increment(debug);
+                    debug.log("GVN applied and new node is %1s", newNode);
                     return true;
                 }
             }
@@ -319,7 +321,7 @@
                     }
                 }
                 if (nodeClass.isCanonicalizable()) {
-                    COUNTER_CANONICALIZATION_CONSIDERED_NODES.increment();
+                    COUNTER_CANONICALIZATION_CONSIDERED_NODES.increment(debug);
                     Node canonical;
                     try (AutoCloseable verify = getCanonicalizeableContractAssertion(node)) {
                         canonical = ((Canonicalizable) node).canonical(tool);
@@ -335,8 +337,8 @@
                 }
 
                 if (nodeClass.isSimplifiable() && simplify) {
-                    Debug.log(Debug.VERBOSE_LEVEL, "Canonicalizer: simplifying %s", node);
-                    COUNTER_SIMPLIFICATION_CONSIDERED_NODES.increment();
+                    debug.log(DebugContext.VERBOSE_LEVEL, "Canonicalizer: simplifying %s", node);
+                    COUNTER_SIMPLIFICATION_CONSIDERED_NODES.increment(debug);
                     node.simplify(tool);
                     return node.isDeleted();
                 }
@@ -362,12 +364,12 @@
 // @formatter:on
         private boolean performReplacement(final Node node, Node newCanonical) {
             if (newCanonical == node) {
-                Debug.log(Debug.VERBOSE_LEVEL, "Canonicalizer: work on %1s", node);
+                debug.log(DebugContext.VERBOSE_LEVEL, "Canonicalizer: work on %1s", node);
                 return false;
             } else {
                 Node canonical = newCanonical;
-                Debug.log("Canonicalizer: replacing %1s with %1s", node, canonical);
-                COUNTER_CANONICALIZED_NODES.increment();
+                debug.log("Canonicalizer: replacing %1s with %1s", node, canonical);
+                COUNTER_CANONICALIZED_NODES.increment(debug);
                 StructuredGraph graph = (StructuredGraph) node.graph();
                 if (canonical != null && !canonical.isAlive()) {
                     assert !canonical.isDeleted();
@@ -428,9 +430,9 @@
          */
         private boolean tryInferStamp(ValueNode node) {
             if (node.isAlive()) {
-                COUNTER_INFER_STAMP_CALLED.increment();
+                COUNTER_INFER_STAMP_CALLED.increment(debug);
                 if (node.inferStamp()) {
-                    COUNTER_STAMP_CHANGED.increment();
+                    COUNTER_STAMP_CHANGED.increment(debug);
                     for (Node usage : node.usages()) {
                         workList.add(usage);
                     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -35,9 +35,9 @@
 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.debug.Debug;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.graph.NodeStack;
@@ -63,10 +63,10 @@
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.ShortCircuitOrNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.UnaryOpLogicNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
-import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.calc.AndNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
 import org.graalvm.compiler.nodes.calc.BinaryNode;
@@ -96,10 +96,10 @@
 
 public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
 
-    private static final DebugCounter counterStampsRegistered = Debug.counter("StampsRegistered");
-    private static final DebugCounter counterStampsFound = Debug.counter("StampsFound");
-    private static final DebugCounter counterIfsKilled = Debug.counter("CE_KilledIfs");
-    private static final DebugCounter counterPhiStampsImproved = Debug.counter("CE_ImprovedPhis");
+    private static final CounterKey counterStampsRegistered = DebugContext.counter("StampsRegistered");
+    private static final CounterKey counterStampsFound = DebugContext.counter("StampsFound");
+    private static final CounterKey counterIfsKilled = DebugContext.counter("CE_KilledIfs");
+    private static final CounterKey counterPhiStampsImproved = DebugContext.counter("CE_ImprovedPhis");
     private final boolean fullSchedule;
     private final boolean moveGuards;
 
@@ -115,7 +115,7 @@
     @Override
     @SuppressWarnings("try")
     protected void run(StructuredGraph graph, PhaseContext context) {
-        try (Debug.Scope s = Debug.scope("DominatorConditionalElimination")) {
+        try (DebugContext.Scope s = graph.getDebug().scope("DominatorConditionalElimination")) {
             BlockMap<List<Node>> blockToNodes = null;
             NodeMap<Block> nodeToBlock = null;
             ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
@@ -242,6 +242,7 @@
         protected final CanonicalizerTool tool;
         protected final NodeStack undoOperations;
         protected final StructuredGraph graph;
+        protected final DebugContext debug;
         protected final EconomicMap<MergeNode, EconomicMap<ValuePhiNode, PhiInfoElement>> mergeMaps;
 
         /**
@@ -251,6 +252,7 @@
 
         public Instance(StructuredGraph graph, BlockMap<List<Node>> blockToNodes, PhaseContext context) {
             this.graph = graph;
+            this.debug = graph.getDebug();
             this.blockToNodes = blockToNodes;
             this.undoOperations = new NodeStack();
             this.map = graph.createNodeMap();
@@ -304,7 +306,7 @@
                     node.replaceAtPredecessor(deopt);
                     GraphUtil.killCFG(node);
                 }
-                Debug.log("Kill fixed guard guard");
+                debug.log("Kill fixed guard guard");
                 return true;
             })) {
                 registerNewCondition(node.condition(), node.isNegated(), node);
@@ -318,7 +320,7 @@
                 survivingSuccessor.replaceAtPredecessor(null);
                 node.replaceAtPredecessor(survivingSuccessor);
                 GraphUtil.killCFG(node);
-                counterIfsKilled.increment();
+                counterIfsKilled.increment(debug);
                 return true;
             });
         }
@@ -326,7 +328,7 @@
         @Override
         public Integer enter(Block block) {
             int mark = undoOperations.size();
-            Debug.log("[Pre Processing block %s]", block);
+            debug.log("[Pre Processing block %s]", block);
             // For now conservatively collect guards only within the same block.
             pendingTests.clear();
             processNodes(block);
@@ -348,7 +350,7 @@
         private void processBlock(Block block) {
             FixedNode n = block.getBeginNode();
             FixedNode endNode = block.getEndNode();
-            Debug.log("[Processing block %s]", block);
+            debug.log("[Processing block %s]", block);
             while (n != endNode) {
                 if (n.isDeleted() || endNode.isDeleted()) {
                     // This branch was deleted!
@@ -473,7 +475,7 @@
                                 }
                                 newPhi.addInput(valueAt);
                             }
-                            counterPhiStampsImproved.increment();
+                            counterPhiStampsImproved.increment(debug);
                             phi.replaceAtUsagesAndDelete(newPhi);
                         }
                     }
@@ -697,7 +699,7 @@
         }
 
         protected boolean rewireGuards(GuardingNode guard, boolean result, ValueNode proxifiedInput, Stamp guardedValueStamp, GuardRewirer rewireGuardFunction) {
-            counterStampsFound.increment();
+            counterStampsFound.increment(debug);
             return rewireGuardFunction.rewire(guard, result, guardedValueStamp, proxifiedInput);
         }
 
@@ -893,8 +895,8 @@
                     proxiedValue = value;
                 }
                 do {
-                    counterStampsRegistered.increment();
-                    Debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, stamp, guard);
+                    counterStampsRegistered.increment(debug);
+                    debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, stamp, guard);
                     assert value instanceof LogicNode || stamp.isCompatible(value.stamp()) : stamp + " vs. " + value.stamp() + " (" + value + ")";
                     map.setAndGrow(value, new InfoElement(stamp, guard, proxiedValue, map.getAndGrow(value)));
                     undoOperations.push(value);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConvertDeoptimizeToGuardPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConvertDeoptimizeToGuardPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,8 @@
 
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.InputType;
@@ -169,9 +169,10 @@
             return;
         }
 
+        DebugContext debug = deoptBegin.getDebug();
         if (deoptBegin instanceof AbstractMergeNode) {
             AbstractMergeNode mergeNode = (AbstractMergeNode) deoptBegin;
-            Debug.log("Visiting %s", mergeNode);
+            debug.log("Visiting %s", mergeNode);
             FixedNode next = mergeNode.next();
             while (mergeNode.isAlive()) {
                 AbstractEndNode end = mergeNode.forwardEnds().first();
@@ -201,7 +202,7 @@
             }
             survivingSuccessor.replaceAtUsages(InputType.Guard, newGuard);
 
-            Debug.log("Converting deopt on %-5s branch of %s to guard for remaining branch %s.", deoptBegin == ifNode.trueSuccessor() ? "true" : "false", ifNode, survivingSuccessor);
+            debug.log("Converting deopt on %-5s branch of %s to guard for remaining branch %s.", deoptBegin == ifNode.trueSuccessor() ? "true" : "false", ifNode, survivingSuccessor);
             FixedNode next = pred.next();
             pred.setNext(guard);
             guard.setNext(next);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DeadCodeEliminationPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/DeadCodeEliminationPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,16 @@
  */
 package org.graalvm.compiler.phases.common;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeFlood;
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.GuardNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
-import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.phases.Phase;
 
 public class DeadCodeEliminationPhase extends Phase {
@@ -44,7 +44,7 @@
         // @formatter:on
     }
 
-    private static final DebugCounter counterNodesRemoved = Debug.counter("NodesRemoved");
+    private static final CounterKey counterNodesRemoved = DebugContext.counter("NodesRemoved");
 
     public enum Optionality {
         Optional,
@@ -133,11 +133,12 @@
             }
         };
 
+        DebugContext debug = graph.getDebug();
         for (Node node : graph.getNodes()) {
             if (!flood.isMarked(node)) {
                 node.markDeleted();
                 node.applyInputs(consumer);
-                counterNodesRemoved.increment();
+                counterNodesRemoved.increment(debug);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,8 @@
 import org.graalvm.compiler.core.common.type.FloatStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.graph.NodeStack;
@@ -81,13 +81,13 @@
  */
 public class FixReadsPhase extends BasePhase<LowTierContext> {
 
-    private static final DebugCounter counterStampsRegistered = Debug.counter("FixReads_StampsRegistered");
-    private static final DebugCounter counterIfsKilled = Debug.counter("FixReads_KilledIfs");
-    private static final DebugCounter counterConditionalsKilled = Debug.counter("FixReads_KilledConditionals");
-    private static final DebugCounter counterCanonicalizedSwitches = Debug.counter("FixReads_CanonicalizedSwitches");
-    private static final DebugCounter counterConstantReplacements = Debug.counter("FixReads_ConstantReplacement");
-    private static final DebugCounter counterConstantInputReplacements = Debug.counter("FixReads_ConstantInputReplacement");
-    private static final DebugCounter counterBetterMergedStamps = Debug.counter("FixReads_BetterMergedStamp");
+    private static final CounterKey counterStampsRegistered = DebugContext.counter("FixReads_StampsRegistered");
+    private static final CounterKey counterIfsKilled = DebugContext.counter("FixReads_KilledIfs");
+    private static final CounterKey counterConditionalsKilled = DebugContext.counter("FixReads_KilledConditionals");
+    private static final CounterKey counterCanonicalizedSwitches = DebugContext.counter("FixReads_CanonicalizedSwitches");
+    private static final CounterKey counterConstantReplacements = DebugContext.counter("FixReads_ConstantReplacement");
+    private static final CounterKey counterConstantInputReplacements = DebugContext.counter("FixReads_ConstantInputReplacement");
+    private static final CounterKey counterBetterMergedStamps = DebugContext.counter("FixReads_BetterMergedStamp");
 
     protected boolean replaceInputsWithConstants;
     protected Phase schedulePhase;
@@ -137,9 +137,11 @@
         private final boolean replaceConstantInputs;
         private final BlockMap<Integer> blockActionStart;
         private final EconomicMap<MergeNode, EconomicMap<ValueNode, Stamp>> endMaps;
+        private final DebugContext debug;
 
         protected RawConditionalEliminationVisitor(StructuredGraph graph, ScheduleResult schedule, MetaAccessProvider metaAccess, boolean replaceInputsWithConstants) {
             this.graph = graph;
+            this.debug = graph.getDebug();
             this.schedule = schedule;
             this.metaAccess = metaAccess;
             blockActionStart = new BlockMap<>(schedule.getCFG());
@@ -174,7 +176,7 @@
                                         continue;
                                     }
                                 }
-                                counterConstantInputReplacements.increment();
+                                counterConstantInputReplacements.increment(node.getDebug());
                                 ConstantNode stampConstant = ConstantNode.forConstant(bestStamp, constant, metaAccess, graph);
                                 assert stampConstant.stamp().isCompatible(valueNode.stamp());
                                 replaceInput(p, node, stampConstant);
@@ -220,7 +222,7 @@
             MapCursor<ValueNode, Stamp> entries = endMap.getEntries();
             while (entries.advance()) {
                 if (registerNewValueStamp(entries.getKey(), entries.getValue())) {
-                    counterBetterMergedStamps.increment();
+                    counterBetterMergedStamps.increment(debug);
                 }
             }
         }
@@ -320,8 +322,8 @@
             Constant constant = newStamp.asConstant();
             if (constant != null && !(node instanceof ConstantNode)) {
                 ConstantNode stampConstant = ConstantNode.forConstant(newStamp, constant, metaAccess, graph);
-                Debug.log("RawConditionElimination: constant stamp replaces %1s with %1s", node, stampConstant);
-                counterConstantReplacements.increment();
+                debug.log("RawConditionElimination: constant stamp replaces %1s with %1s", node, stampConstant);
+                counterConstantReplacements.increment(debug);
                 node.replaceAtUsages(InputType.Value, stampConstant);
                 GraphUtil.tryKillUnused(node);
                 return true;
@@ -341,8 +343,8 @@
         protected void processIntegerSwitch(IntegerSwitchNode node) {
             Stamp bestStamp = getBestStamp(node.value());
             if (node.tryRemoveUnreachableKeys(null, bestStamp)) {
-                Debug.log("\t Canonicalized integer switch %s for value %s and stamp %s", node, node.value(), bestStamp);
-                counterCanonicalizedSwitches.increment();
+                debug.log("\t Canonicalized integer switch %s for value %s and stamp %s", node, node.value(), bestStamp);
+                counterCanonicalizedSwitches.increment(debug);
             }
         }
 
@@ -356,7 +358,7 @@
                 node.replaceAtPredecessor(survivingSuccessor);
                 GraphUtil.killCFG(node);
 
-                counterIfsKilled.increment();
+                counterIfsKilled.increment(debug);
             }
         }
 
@@ -364,7 +366,7 @@
             TriState result = tryProveCondition(node.condition());
             if (result != TriState.UNKNOWN) {
                 boolean isTrue = (result == TriState.TRUE);
-                counterConditionalsKilled.increment();
+                counterConditionalsKilled.increment(debug);
                 node.replaceAndDelete(isTrue ? node.trueValue() : node.falseValue());
             } else {
                 Stamp trueStamp = getBestStamp(node.trueValue());
@@ -444,8 +446,8 @@
         }
 
         protected void registerNewStamp(ValueNode value, Stamp newStamp) {
-            counterStampsRegistered.increment();
-            Debug.log("\t Saving stamp for node %s stamp %s", value, newStamp);
+            counterStampsRegistered.increment(debug);
+            debug.log("\t Saving stamp for node %s stamp %s", value, newStamp);
             ValueNode originalNode = value;
             stampMap.setAndGrow(originalNode, new StampElement(newStamp, stampMap.getAndGrow(originalNode)));
             undoOperations.push(originalNode);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/GuardLoweringPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/GuardLoweringPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,8 +23,8 @@
 package org.graalvm.compiler.phases.common;
 
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.BeginNode;
@@ -139,6 +139,7 @@
     }
 
     private static void processBlock(Block block, ScheduleResult schedule) {
-        new LowerGuards(block, Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()).processNodes(block, schedule);
+        DebugContext debug = block.getBeginNode().getDebug();
+        new LowerGuards(block, debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()).processNodes(block, schedule);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/UseTrappingNullChecksPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/UseTrappingNullChecksPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,8 @@
 
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
@@ -58,10 +58,10 @@
 
 public class UseTrappingNullChecksPhase extends BasePhase<LowTierContext> {
 
-    private static final DebugCounter counterTrappingNullCheck = Debug.counter("TrappingNullCheck");
-    private static final DebugCounter counterTrappingNullCheckExistingRead = Debug.counter("TrappingNullCheckExistingRead");
-    private static final DebugCounter counterTrappingNullCheckUnreached = Debug.counter("TrappingNullCheckUnreached");
-    private static final DebugCounter counterTrappingNullCheckDynamicDeoptimize = Debug.counter("TrappingNullCheckDynamicDeoptimize");
+    private static final CounterKey counterTrappingNullCheck = DebugContext.counter("TrappingNullCheck");
+    private static final CounterKey counterTrappingNullCheckExistingRead = DebugContext.counter("TrappingNullCheckExistingRead");
+    private static final CounterKey counterTrappingNullCheckUnreached = DebugContext.counter("TrappingNullCheckUnreached");
+    private static final CounterKey counterTrappingNullCheckDynamicDeoptimize = DebugContext.counter("TrappingNullCheckDynamicDeoptimize");
 
     @Override
     protected void run(StructuredGraph graph, LowTierContext context) {
@@ -175,12 +175,13 @@
     }
 
     private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason, long implicitNullCheckLimit) {
-        counterTrappingNullCheck.increment();
+        DebugContext debug = deopt.getDebug();
+        counterTrappingNullCheck.increment(debug);
         if (deopt instanceof DynamicDeoptimizeNode) {
-            counterTrappingNullCheckDynamicDeoptimize.increment();
+            counterTrappingNullCheckDynamicDeoptimize.increment(debug);
         }
         if (deoptimizationReason == DeoptimizationReason.UnreachedCode) {
-            counterTrappingNullCheckUnreached.increment();
+            counterTrappingNullCheckUnreached.increment(debug);
         }
         IsNullNode isNullNode = (IsNullNode) condition;
         AbstractBeginNode nonTrappingContinuation = ifNode.falseSuccessor();
@@ -202,7 +203,7 @@
                         fixedAccessNode.setNullCheck(true);
                         deopt.graph().removeSplit(ifNode, nonTrappingContinuation);
                         trappingNullCheck = fixedAccessNode;
-                        counterTrappingNullCheckExistingRead.increment();
+                        counterTrappingNullCheckExistingRead.increment(debug);
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,25 +22,24 @@
  */
 package org.graalvm.compiler.phases.common.inlining;
 
-import jdk.vm.ci.code.BytecodeFrame;
-import jdk.vm.ci.meta.Assumptions;
-import jdk.vm.ci.meta.DeoptimizationAction;
-import jdk.vm.ci.meta.DeoptimizationReason;
-import jdk.vm.ci.meta.JavaConstant;
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.ResolvedJavaType;
+import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile;
+import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException;
+import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
 import org.graalvm.compiler.core.common.util.Util;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
@@ -97,15 +96,14 @@
 import org.graalvm.util.UnmodifiableEconomicMap;
 import org.graalvm.util.UnmodifiableMapCursor;
 
-import java.lang.reflect.Constructor;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile;
-import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException;
-import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining;
+import jdk.vm.ci.code.BytecodeFrame;
+import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.DeoptimizationAction;
+import jdk.vm.ci.meta.DeoptimizationReason;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
 
 public class InliningUtil extends ValueMergeUtil {
 
@@ -135,24 +133,26 @@
     public static void logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
         if (allowLogging) {
             printInlining(info, inliningDepth, success, msg, args);
-            if (shouldLogInliningDecision()) {
-                logInliningDecision(methodName(info), success, msg, args);
+            DebugContext debug = info.graph().getDebug();
+            if (shouldLogInliningDecision(debug)) {
+                logInliningDecision(debug, methodName(info), success, msg, args);
             }
         }
     }
 
     @SuppressWarnings("try")
-    public static void logInliningDecision(final String msg, final Object... args) {
-        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
+    public static void logInliningDecision(DebugContext debug, final String msg, final Object... args) {
+        try (DebugContext.Scope s = debug.scope(inliningDecisionsScopeString)) {
             // Can't use log here since we are varargs
-            if (Debug.isLogEnabled()) {
-                Debug.logv(msg, args);
+            if (debug.isLogEnabled()) {
+                debug.logv(msg, args);
             }
         }
     }
 
     public static void logNotInlinedMethod(Invoke invoke, String msg) {
-        if (shouldLogInliningDecision()) {
+        DebugContext debug = invoke.asNode().getDebug();
+        if (shouldLogInliningDecision(debug)) {
             String methodString = invoke.toString();
             if (invoke.callTarget() == null) {
                 methodString += " callTarget=null";
@@ -162,7 +162,7 @@
                     methodString += " " + targetName;
                 }
             }
-            logInliningDecision(methodString, false, msg, new Object[0]);
+            logInliningDecision(debug, methodString, false, msg, new Object[0]);
         }
     }
 
@@ -171,25 +171,26 @@
     }
 
     public static void logNotInlinedInvoke(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
+        DebugContext debug = invoke.asNode().getDebug();
         printInlining(method, invoke, inliningDepth, false, msg, args);
-        if (shouldLogInliningDecision()) {
+        if (shouldLogInliningDecision(debug)) {
             String methodString = methodName(method, invoke);
-            logInliningDecision(methodString, false, msg, args);
+            logInliningDecision(debug, methodString, false, msg, args);
         }
     }
 
-    private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) {
+    private static void logInliningDecision(DebugContext debug, final String methodString, final boolean success, final String msg, final Object... args) {
         String inliningMsg = "inlining " + methodString + ": " + msg;
         if (!success) {
             inliningMsg = "not " + inliningMsg;
         }
-        logInliningDecision(inliningMsg, args);
+        logInliningDecision(debug, inliningMsg, args);
     }
 
     @SuppressWarnings("try")
-    public static boolean shouldLogInliningDecision() {
-        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
-            return Debug.isLogEnabled();
+    public static boolean shouldLogInliningDecision(DebugContext debug) {
+        try (DebugContext.Scope s = debug.scope(inliningDecisionsScopeString)) {
+            return debug.isLogEnabled();
         }
     }
 
@@ -278,121 +279,115 @@
     public static UnmodifiableEconomicMap<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck, ResolvedJavaMethod inlineeMethod) {
         FixedNode invokeNode = invoke.asNode();
         StructuredGraph graph = invokeNode.graph();
-        MethodMetricsInlineeScopeInfo m = MethodMetricsInlineeScopeInfo.create(graph.getOptions());
-        try (Debug.Scope s = Debug.methodMetricsScope("InlineEnhancement", m, false)) {
-            final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
+        final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
 
-            assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
-            assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase";
+        assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
+        assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase";
 
-            if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) {
-                nonNullReceiver(invoke);
-            }
+        if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) {
+            nonNullReceiver(invoke);
+        }
 
-            ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
-            ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
-            ArrayList<Invoke> partialIntrinsicExits = new ArrayList<>();
-            UnwindNode unwindNode = null;
-            final StartNode entryPointNode = inlineGraph.start();
-            FixedNode firstCFGNode = entryPointNode.next();
-            if (firstCFGNode == null) {
-                throw new IllegalStateException("Inlined graph is in invalid state: " + inlineGraph);
-            }
-            for (Node node : inlineGraph.getNodes()) {
-                if (node == entryPointNode || (node == entryPointNode.stateAfter() && node.usages().count() == 1) || node instanceof ParameterNode) {
-                    // Do nothing.
-                } else {
-                    nodes.add(node);
-                    if (node instanceof ReturnNode) {
-                        returnNodes.add((ReturnNode) node);
-                    } else if (node instanceof Invoke) {
-                        Invoke invokeInInlineGraph = (Invoke) node;
-                        if (invokeInInlineGraph.bci() == BytecodeFrame.UNKNOWN_BCI) {
-                            ResolvedJavaMethod target1 = inlineeMethod;
-                            ResolvedJavaMethod target2 = invokeInInlineGraph.callTarget().targetMethod();
-                            assert target1.equals(target2) : String.format("invoke in inlined method expected to be partial intrinsic exit (i.e., call to %s), not a call to %s",
-                                            target1.format("%H.%n(%p)"), target2.format("%H.%n(%p)"));
-                            partialIntrinsicExits.add(invokeInInlineGraph);
-                        }
-                    } else if (node instanceof UnwindNode) {
-                        assert unwindNode == null;
-                        unwindNode = (UnwindNode) node;
+        ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
+        ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
+        ArrayList<Invoke> partialIntrinsicExits = new ArrayList<>();
+        UnwindNode unwindNode = null;
+        final StartNode entryPointNode = inlineGraph.start();
+        FixedNode firstCFGNode = entryPointNode.next();
+        if (firstCFGNode == null) {
+            throw new IllegalStateException("Inlined graph is in invalid state: " + inlineGraph);
+        }
+        for (Node node : inlineGraph.getNodes()) {
+            if (node == entryPointNode || (node == entryPointNode.stateAfter() && node.usages().count() == 1) || node instanceof ParameterNode) {
+                // Do nothing.
+            } else {
+                nodes.add(node);
+                if (node instanceof ReturnNode) {
+                    returnNodes.add((ReturnNode) node);
+                } else if (node instanceof Invoke) {
+                    Invoke invokeInInlineGraph = (Invoke) node;
+                    if (invokeInInlineGraph.bci() == BytecodeFrame.UNKNOWN_BCI) {
+                        ResolvedJavaMethod target1 = inlineeMethod;
+                        ResolvedJavaMethod target2 = invokeInInlineGraph.callTarget().targetMethod();
+                        assert target1.equals(target2) : String.format("invoke in inlined method expected to be partial intrinsic exit (i.e., call to %s), not a call to %s",
+                                        target1.format("%H.%n(%p)"), target2.format("%H.%n(%p)"));
+                        partialIntrinsicExits.add(invokeInInlineGraph);
                     }
+                } else if (node instanceof UnwindNode) {
+                    assert unwindNode == null;
+                    unwindNode = (UnwindNode) node;
                 }
             }
+        }
 
-            final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invokeNode);
-            DuplicationReplacement localReplacement = new DuplicationReplacement() {
+        final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invokeNode);
+        DuplicationReplacement localReplacement = new DuplicationReplacement() {
 
-                @Override
-                public Node replacement(Node node) {
-                    if (node instanceof ParameterNode) {
-                        return parameters.get(((ParameterNode) node).index());
-                    } else if (node == entryPointNode) {
-                        return prevBegin;
-                    }
-                    return node;
+            @Override
+            public Node replacement(Node node) {
+                if (node instanceof ParameterNode) {
+                    return parameters.get(((ParameterNode) node).index());
+                } else if (node == entryPointNode) {
+                    return prevBegin;
                 }
-            };
+                return node;
+            }
+        };
+
+        assert invokeNode.successors().first() != null : invoke;
+        assert invokeNode.predecessor() != null;
+
+        EconomicMap<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
 
-            assert invokeNode.successors().first() != null : invoke;
-            assert invokeNode.predecessor() != null;
-
-            EconomicMap<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
+        FrameState stateAfter = invoke.stateAfter();
+        assert stateAfter == null || stateAfter.isAlive();
 
-            FrameState stateAfter = invoke.stateAfter();
-            assert stateAfter == null || stateAfter.isAlive();
+        FrameState stateAtExceptionEdge = null;
+        if (invoke instanceof InvokeWithExceptionNode) {
+            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
+            if (unwindNode != null) {
+                ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
+                stateAtExceptionEdge = obj.stateAfter();
+            }
+        }
 
-            FrameState stateAtExceptionEdge = null;
-            if (invoke instanceof InvokeWithExceptionNode) {
-                InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
-                if (unwindNode != null) {
-                    ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                    stateAtExceptionEdge = obj.stateAfter();
+        updateSourcePositions(invoke, inlineGraph, duplicates);
+        if (stateAfter != null) {
+            processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
+            int callerLockDepth = stateAfter.nestedLockDepth();
+            if (callerLockDepth != 0) {
+                for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) {
+                    MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
+                    processMonitorId(invoke.stateAfter(), monitor);
                 }
             }
+        } else {
+            assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
+        }
 
-            updateSourcePositions(invoke, inlineGraph, duplicates);
-            if (stateAfter != null) {
-                processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
-                int callerLockDepth = stateAfter.nestedLockDepth();
-                if (callerLockDepth != 0) {
-                    for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) {
-                        MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
-                        processMonitorId(invoke.stateAfter(), monitor);
-                    }
-                }
+        firstCFGNode = (FixedNode) duplicates.get(firstCFGNode);
+        for (int i = 0; i < returnNodes.size(); i++) {
+            returnNodes.set(i, (ReturnNode) duplicates.get(returnNodes.get(i)));
+        }
+        for (Invoke exit : partialIntrinsicExits) {
+            // A partial intrinsic exit must be replaced with a call to
+            // the intrinsified method.
+            Invoke dup = (Invoke) duplicates.get(exit.asNode());
+            if (dup instanceof InvokeNode) {
+                InvokeNode repl = graph.add(new InvokeNode(invoke.callTarget(), invoke.bci()));
+                dup.intrinsify(repl.asNode());
             } else {
-                assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
-            }
-
-            firstCFGNode = (FixedNode) duplicates.get(firstCFGNode);
-            for (int i = 0; i < returnNodes.size(); i++) {
-                returnNodes.set(i, (ReturnNode) duplicates.get(returnNodes.get(i)));
+                ((InvokeWithExceptionNode) dup).replaceWithNewBci(invoke.bci());
             }
-            for (Invoke exit : partialIntrinsicExits) {
-                // A partial intrinsic exit must be replaced with a call to
-                // the intrinsified method.
-                Invoke dup = (Invoke) duplicates.get(exit.asNode());
-                if (dup instanceof InvokeNode) {
-                    InvokeNode repl = graph.add(new InvokeNode(invoke.callTarget(), invoke.bci()));
-                    dup.intrinsify(repl.asNode());
-                } else {
-                    ((InvokeWithExceptionNode) dup).replaceWithNewBci(invoke.bci());
-                }
-            }
-            if (unwindNode != null) {
-                unwindNode = (UnwindNode) duplicates.get(unwindNode);
-            }
+        }
+        if (unwindNode != null) {
+            unwindNode = (UnwindNode) duplicates.get(unwindNode);
+        }
 
-            finishInlining(invoke, graph, firstCFGNode, returnNodes, unwindNode, inlineGraph.getAssumptions(), inlineGraph);
-            GraphUtil.killCFG(invokeNode);
+        finishInlining(invoke, graph, firstCFGNode, returnNodes, unwindNode, inlineGraph.getAssumptions(), inlineGraph);
+        GraphUtil.killCFG(invokeNode);
 
-            if (Debug.isMethodMeterEnabled() && m != null) {
-                MethodMetricsImpl.recordInlinee(m.getRootMethod(), invoke.asNode().graph().method(), inlineeMethod);
-            }
-            return duplicates;
-        }
+        return duplicates;
     }
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/AbstractInlineInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/AbstractInlineInfo.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,8 +22,6 @@
  */
 package org.graalvm.compiler.phases.common.inlining.info;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -66,10 +64,8 @@
     @SuppressWarnings("try")
     public final void populateInlinableElements(HighTierContext context, StructuredGraph caller, CanonicalizerPhase canonicalizer, OptionValues options) {
         for (int i = 0; i < numberOfMethods(); i++) {
-            try (Debug.Scope s = Debug.methodMetricsScope("InlineEnhancement", MethodMetricsInlineeScopeInfo.create(options), false)) {
-                Inlineable elem = Inlineable.getInlineableElement(methodAt(i), invoke, context, canonicalizer);
-                setInlinableElement(i, elem);
-            }
+            Inlineable elem = Inlineable.getInlineableElement(methodAt(i), invoke, context, canonicalizer);
+            setInlinableElement(i, elem);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,7 +26,6 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
@@ -342,7 +341,7 @@
         ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
         LoadHubNode hub = graph.unique(new LoadHubNode(stampProvider, nonNullReceiver));
 
-        Debug.log("Type switch with %d types", concretes.size());
+        graph.getDebug().log("Type switch with %d types", concretes.size());
 
         ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()];
         double[] keyProbabilities = new double[ptypes.size() + 1];
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,7 +28,7 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodes.ConstantNode;
@@ -69,7 +69,7 @@
     public InlineableGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
         StructuredGraph original = getOriginalGraph(method, context, canonicalizer, invoke.asNode().graph(), invoke.bci());
         // TODO copying the graph is only necessary if it is modified or if it contains any invokes
-        this.graph = (StructuredGraph) original.copy();
+        this.graph = (StructuredGraph) original.copy(invoke.asNode().getDebug());
         specializeGraphToArguments(invoke, context, canonicalizer);
     }
 
@@ -92,7 +92,8 @@
      */
     @SuppressWarnings("try")
     private boolean specializeGraphToArguments(final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
-        try (Debug.Scope s = Debug.scope("InlineGraph", graph)) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("InlineGraph", graph)) {
 
             ArrayList<Node> parameterUsages = replaceParamsWithMoreInformativeArguments(invoke, context);
             if (parameterUsages != null) {
@@ -107,7 +108,7 @@
             }
 
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -193,8 +194,9 @@
      */
     @SuppressWarnings("try")
     private static StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer, StructuredGraph caller) {
-        StructuredGraph newGraph = new StructuredGraph.Builder(caller.getOptions(), AllowAssumptions.ifNonNull(caller.getAssumptions())).method(method).build();
-        try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) {
+        DebugContext debug = caller.getDebug();
+        StructuredGraph newGraph = new StructuredGraph.Builder(caller.getOptions(), debug, AllowAssumptions.ifNonNull(caller.getAssumptions())).method(method).build();
+        try (DebugContext.Scope s = debug.scope("InlineGraph", newGraph)) {
             if (!caller.isUnsafeAccessTrackingEnabled()) {
                 newGraph.disableUnsafeAccessTracking();
             }
@@ -209,7 +211,7 @@
 
             return newGraph;
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/GreedyInliningPolicy.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/GreedyInliningPolicy.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,8 +31,8 @@
 
 import java.util.Map;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.spi.Replacements;
@@ -43,7 +43,7 @@
 
 public class GreedyInliningPolicy extends AbstractInliningPolicy {
 
-    private static final DebugCounter inliningStoppedByMaxDesiredSizeCounter = Debug.counter("InliningStoppedByMaxDesiredSize");
+    private static final CounterKey inliningStoppedByMaxDesiredSizeCounter = DebugContext.counter("InliningStoppedByMaxDesiredSize");
 
     public GreedyInliningPolicy(Map<Invoke, Double> hints) {
         super(hints);
@@ -52,8 +52,9 @@
     @Override
     public boolean continueInlining(StructuredGraph currentGraph) {
         if (InliningUtil.getNodeCount(currentGraph) >= MaximumDesiredSize.getValue(currentGraph.getOptions())) {
-            InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
-            inliningStoppedByMaxDesiredSizeCounter.increment();
+            DebugContext debug = currentGraph.getDebug();
+            InliningUtil.logInliningDecision(debug, "inlining is cut off by MaximumDesiredSize");
+            inliningStoppedByMaxDesiredSizeCounter.increment(debug);
             return false;
         }
         return true;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java	Fri Jul 07 09:40:47 2017 -0700
@@ -35,10 +35,9 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.type.ObjectStamp;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.CallTargetNode;
@@ -101,9 +100,9 @@
 public class InliningData {
 
     // Counters
-    private static final DebugCounter counterInliningPerformed = Debug.counter("InliningPerformed");
-    private static final DebugCounter counterInliningRuns = Debug.counter("InliningRuns");
-    private static final DebugCounter counterInliningConsidered = Debug.counter("InliningConsidered");
+    private static final CounterKey counterInliningPerformed = DebugContext.counter("InliningPerformed");
+    private static final CounterKey counterInliningRuns = DebugContext.counter("InliningRuns");
+    private static final CounterKey counterInliningConsidered = DebugContext.counter("InliningConsidered");
 
     /**
      * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
@@ -116,6 +115,7 @@
     private final CanonicalizerPhase canonicalizer;
     private final InliningPolicy inliningPolicy;
     private final StructuredGraph rootGraph;
+    private final DebugContext debug;
 
     private int maxGraphs;
 
@@ -127,6 +127,7 @@
         this.inliningPolicy = inliningPolicy;
         this.maxGraphs = 1;
         this.rootGraph = rootGraph;
+        this.debug = rootGraph.getDebug();
 
         invocationQueue.push(new MethodInvocation(null, 1.0, 1.0, null));
         graphQueue.push(new CallsiteHolderExplorable(rootGraph, 1.0, 1.0, null, rootInvokes));
@@ -395,14 +396,13 @@
         StructuredGraph callerGraph = callerCallsiteHolder.graph();
         InlineInfo calleeInfo = calleeInvocation.callee();
         try {
-            OptionValues options = rootGraph.getOptions();
-            try (Debug.Scope scope = Debug.scope("doInline", callerGraph); Debug.Scope s = Debug.methodMetricsScope("InlineEnhancement", MethodMetricsInlineeScopeInfo.create(options), false)) {
+            try (DebugContext.Scope scope = debug.scope("doInline", callerGraph)) {
                 EconomicSet<Node> canonicalizedNodes = EconomicSet.create(Equivalence.IDENTITY);
                 canonicalizedNodes.addAll(calleeInfo.invoke().asNode().usages());
                 EconomicSet<Node> parameterUsages = calleeInfo.inline(new Providers(context));
                 canonicalizedNodes.addAll(parameterUsages);
-                counterInliningRuns.increment();
-                Debug.dump(Debug.DETAILED_LEVEL, callerGraph, "after %s", calleeInfo);
+                counterInliningRuns.increment(debug);
+                debug.dump(DebugContext.DETAILED_LEVEL, callerGraph, "after %s", calleeInfo);
 
                 Graph.Mark markBeforeCanonicalization = callerGraph.getMark();
 
@@ -417,7 +417,7 @@
 
                 callerCallsiteHolder.computeProbabilities();
 
-                counterInliningPerformed.increment();
+                counterInliningPerformed.increment(debug);
             }
         } catch (BailoutException bailout) {
             throw bailout;
@@ -426,7 +426,7 @@
         } catch (GraalError e) {
             throw e.addContext(calleeInfo.toString());
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
     }
 
@@ -446,7 +446,7 @@
         CallsiteHolderExplorable callerCallsiteHolder = (CallsiteHolderExplorable) currentGraph();
         InlineInfo calleeInfo = calleeInvocation.callee();
         assert callerCallsiteHolder.containsInvoke(calleeInfo.invoke());
-        counterInliningConsidered.increment();
+        counterInliningConsidered.increment(debug);
 
         if (inliningPolicy.isWorthInlining(context.getReplacements(), calleeInvocation, inliningDepth, true)) {
             doInline(callerCallsiteHolder, calleeInvocation);
@@ -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_LEVEL)) {
+        if (!debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
             return NO_CONTEXT;
         }
         Object[] result = new Object[graphQueue.size()];
@@ -737,14 +737,14 @@
              * "all concrete methods that come into question already had the callees they contain analyzed for inlining"
              */
             popInvocation();
-            try (Debug.Scope s = Debug.scope("Inlining", inliningContext())) {
+            try (DebugContext.Scope s = debug.scope("Inlining", inliningContext())) {
                 if (tryToInline(currentInvocation, inliningDepth() + 1)) {
                     // Report real progress only if we inline into the root graph
                     return currentGraph().graph() == rootGraph;
                 }
                 return false;
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,13 +24,12 @@
 
 import java.util.regex.Pattern;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugCounter;
-import org.graalvm.compiler.debug.DebugMemUseTracker;
-import org.graalvm.compiler.debug.DebugTimer;
-import org.graalvm.compiler.debug.GraalDebugConfig;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.debug.MemUseTrackerKey;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Graph.Mark;
 import org.graalvm.compiler.graph.Graph.NodeEvent;
@@ -62,23 +61,23 @@
     /**
      * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}.
      */
-    private final DebugTimer timer;
+    private final TimerKey timer;
 
     /**
      * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}.
      */
-    private final DebugCounter executionCount;
+    private final CounterKey executionCount;
 
     /**
      * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to
      * {@link #apply(StructuredGraph, Object, boolean)}.
      */
-    private final DebugCounter inputNodesCount;
+    private final CounterKey inputNodesCount;
 
     /**
      * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}.
      */
-    private final DebugMemUseTracker memUseTracker;
+    private final MemUseTrackerKey memUseTracker;
 
     /** Lazy initialization to create pattern only when assertions are enabled. */
     static class NamePatternHolder {
@@ -89,29 +88,29 @@
         /**
          * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}.
          */
-        private final DebugTimer timer;
+        private final TimerKey timer;
 
         /**
          * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}.
          */
-        private final DebugCounter executionCount;
+        private final CounterKey executionCount;
 
         /**
          * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to
          * {@link #apply(StructuredGraph, Object, boolean)}.
          */
-        private final DebugCounter inputNodesCount;
+        private final CounterKey inputNodesCount;
 
         /**
          * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}.
          */
-        private final DebugMemUseTracker memUseTracker;
+        private final MemUseTrackerKey memUseTracker;
 
         public BasePhaseStatistics(Class<?> clazz) {
-            timer = Debug.timer("PhaseTime_%s", clazz);
-            executionCount = Debug.counter("PhaseCount_%s", clazz);
-            memUseTracker = Debug.memUseTracker("PhaseMemUse_%s", clazz);
-            inputNodesCount = Debug.counter("PhaseNodes_%s", clazz);
+            timer = DebugContext.timer("PhaseTime_%s", clazz).doc("Time spent in phase.");
+            executionCount = DebugContext.counter("PhaseCount_%s", clazz).doc("Number of phase executions.");
+            memUseTracker = DebugContext.memUseTracker("PhaseMemUse_%s", clazz).doc("Memory allocated in phase.");
+            inputNodesCount = DebugContext.counter("PhaseNodes_%s", clazz).doc("Number of nodes input to phase.");
         }
     }
 
@@ -138,8 +137,8 @@
         apply(graph, context, true);
     }
 
-    private BasePhase<?> getEnclosingPhase() {
-        for (Object c : Debug.context()) {
+    private BasePhase<?> getEnclosingPhase(DebugContext debug) {
+        for (Object c : debug.context()) {
             if (c != this && c instanceof BasePhase) {
                 if (!(c instanceof PhaseSuite)) {
                     return (BasePhase<?>) c;
@@ -150,16 +149,17 @@
     }
 
     private boolean dumpBefore(final StructuredGraph graph, final C context, boolean isTopLevel) {
-        if (isTopLevel && (Debug.isDumpEnabled(Debug.VERBOSE_LEVEL) || shouldDumpBeforeAtBasicLevel() && Debug.isDumpEnabled(Debug.BASIC_LEVEL))) {
+        DebugContext debug = graph.getDebug();
+        if (isTopLevel && (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL) || shouldDumpBeforeAtBasicLevel() && debug.isDumpEnabled(DebugContext.BASIC_LEVEL))) {
             if (shouldDumpBeforeAtBasicLevel()) {
-                Debug.dump(Debug.BASIC_LEVEL, graph, "Before phase %s", getName());
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Before phase %s", getName());
             } else {
-                Debug.dump(Debug.VERBOSE_LEVEL, graph, "Before phase %s", getName());
+                debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Before phase %s", getName());
             }
-        } else if (!isTopLevel && Debug.isDumpEnabled(Debug.VERBOSE_LEVEL + 1)) {
-            Debug.dump(Debug.VERBOSE_LEVEL + 1, graph, "Before subphase %s", getName());
-        } else if (Debug.isDumpEnabled(Debug.ENABLED_LEVEL) && shouldDump(graph, context)) {
-            Debug.dump(Debug.ENABLED_LEVEL, graph, "Before %s %s", isTopLevel ? "phase" : "subphase", getName());
+        } else if (!isTopLevel && debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL + 1)) {
+            debug.dump(DebugContext.VERBOSE_LEVEL + 1, graph, "Before subphase %s", getName());
+        } else if (debug.isDumpEnabled(DebugContext.ENABLED_LEVEL) && shouldDump(graph, context)) {
+            debug.dump(DebugContext.ENABLED_LEVEL, graph, "Before %s %s", isTopLevel ? "phase" : "subphase", getName());
             return true;
         }
         return false;
@@ -176,7 +176,8 @@
     @SuppressWarnings("try")
     protected final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) {
         graph.checkCancellation();
-        try (DebugCloseable a = timer.start(); Scope s = Debug.scope(getClass(), this); DebugCloseable c = memUseTracker.start()) {
+        DebugContext debug = graph.getDebug();
+        try (DebugCloseable a = timer.start(debug); DebugContext.Scope s = debug.scope(getClass(), this); DebugCloseable c = memUseTracker.start(debug)) {
             int sizeBefore = 0;
             Mark before = null;
             OptionValues options = graph.getOptions();
@@ -185,14 +186,14 @@
                 sizeBefore = NodeCostUtil.computeGraphSize(graph);
                 before = graph.getMark();
             }
-            boolean isTopLevel = getEnclosingPhase() == null;
+            boolean isTopLevel = getEnclosingPhase(graph.getDebug()) == null;
             boolean dumpedBefore = false;
-            if (dumpGraph && Debug.isEnabled()) {
+            if (dumpGraph && debug.areScopesEnabled()) {
                 dumpedBefore = dumpBefore(graph, context, isTopLevel);
             }
-            inputNodesCount.add(graph.getNodeCount());
+            inputNodesCount.add(debug, graph.getNodeCount());
             this.run(graph, context);
-            executionCount.increment();
+            executionCount.increment(debug);
             if (verifySizeContract) {
                 if (!before.isCurrent()) {
                     int sizeAfter = NodeCostUtil.computeGraphSize(graph);
@@ -200,54 +201,56 @@
                 }
             }
 
-            if (dumpGraph && Debug.isEnabled()) {
+            if (dumpGraph && debug.areScopesEnabled()) {
                 dumpAfter(graph, isTopLevel, dumpedBefore);
             }
-            if (Debug.isVerifyEnabled()) {
-                Debug.verify(graph, "%s", getName());
+            if (debug.isVerifyEnabled()) {
+                debug.verify(graph, "%s", getName());
             }
             assert graph.verify();
         } catch (Throwable t) {
-            throw Debug.handle(t);
+            throw debug.handle(t);
         }
     }
 
     private void dumpAfter(final StructuredGraph graph, boolean isTopLevel, boolean dumpedBefore) {
         boolean dumped = false;
+        DebugContext debug = graph.getDebug();
         if (isTopLevel) {
             if (shouldDumpAfterAtBasicLevel()) {
-                if (Debug.isDumpEnabled(Debug.BASIC_LEVEL)) {
-                    Debug.dump(Debug.BASIC_LEVEL, graph, "After phase %s", getName());
+                if (debug.isDumpEnabled(DebugContext.BASIC_LEVEL)) {
+                    debug.dump(DebugContext.BASIC_LEVEL, graph, "After phase %s", getName());
                     dumped = true;
                 }
             } else {
-                if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
-                    Debug.dump(Debug.INFO_LEVEL, graph, "After phase %s", getName());
+                if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
+                    debug.dump(DebugContext.INFO_LEVEL, graph, "After phase %s", getName());
                     dumped = true;
                 }
             }
         } else {
-            if (Debug.isDumpEnabled(Debug.INFO_LEVEL + 1)) {
-                Debug.dump(Debug.INFO_LEVEL + 1, graph, "After subphase %s", getName());
+            if (debug.isDumpEnabled(DebugContext.INFO_LEVEL + 1)) {
+                debug.dump(DebugContext.INFO_LEVEL + 1, graph, "After subphase %s", getName());
                 dumped = true;
             }
         }
-        if (!dumped && Debug.isDumpEnabled(Debug.ENABLED_LEVEL) && dumpedBefore) {
-            Debug.dump(Debug.ENABLED_LEVEL, graph, "After %s %s", isTopLevel ? "phase" : "subphase", getName());
+        if (!dumped && debug.isDumpEnabled(DebugContext.ENABLED_LEVEL) && dumpedBefore) {
+            debug.dump(DebugContext.ENABLED_LEVEL, graph, "After %s %s", isTopLevel ? "phase" : "subphase", getName());
         }
     }
 
     @SuppressWarnings("try")
     private boolean shouldDump(StructuredGraph graph, C context) {
-        String phaseChange = GraalDebugConfig.Options.DumpOnPhaseChange.getValue(graph.getOptions());
-        if (phaseChange != null && getClass().getSimpleName().contains(phaseChange)) {
-            StructuredGraph graphCopy = (StructuredGraph) graph.copy();
+        DebugContext debug = graph.getDebug();
+        String phaseChange = DebugOptions.DumpOnPhaseChange.getValue(graph.getOptions());
+        if (phaseChange != null && Pattern.matches(phaseChange, getClass().getSimpleName())) {
+            StructuredGraph graphCopy = (StructuredGraph) graph.copy(graph.getDebug());
             GraphChangeListener listener = new GraphChangeListener(graphCopy);
             try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) {
-                try (Scope s2 = Debug.sandbox("GraphChangeListener", null)) {
+                try (DebugContext.Scope s2 = debug.sandbox("GraphChangeListener", null)) {
                     run(graphCopy, context);
                 } catch (Throwable t) {
-                    Debug.handle(t);
+                    debug.handle(t);
                 }
             }
             return listener.changed;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/LazyName.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/LazyName.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,11 +22,11 @@
  */
 package org.graalvm.compiler.phases;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 
 /**
  * A name whose {@link String} value is computed only when it is needed. This is useful in
- * combination with debugging facilities such as {@link Debug#scope(Object)} where the
+ * combination with debugging facilities such as {@link DebugContext#scope(Object)} where the
  * {@link String} value of a name is only needed if debugging is enabled.
  */
 public abstract class LazyName implements CharSequence {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/OptimisticOptimizations.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/OptimisticOptimizations.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,6 @@
 import java.util.Set;
 
 import org.graalvm.compiler.core.common.GraalOptions;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
 import org.graalvm.compiler.options.OptionValues;
 
 import jdk.vm.ci.meta.DeoptimizationReason;
@@ -37,7 +35,6 @@
 
     public static final OptimisticOptimizations ALL = new OptimisticOptimizations(EnumSet.allOf(Optimization.class));
     public static final OptimisticOptimizations NONE = new OptimisticOptimizations(EnumSet.noneOf(Optimization.class));
-    private static final DebugCounter disabledOptimisticOptsCounter = Debug.counter("DisabledOptimisticOpts");
 
     public enum Optimization {
         RemoveNeverExecutedCode,
@@ -64,8 +61,6 @@
     private void addOptimization(OptionValues options, ProfilingInfo info, DeoptimizationReason deoptReason, Optimization optimization) {
         if (checkDeoptimizations(options, info, deoptReason)) {
             enabledOpts.add(optimization);
-        } else {
-            disabledOptimisticOptsCounter.increment();
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java	Fri Jul 07 09:40:47 2017 -0700
@@ -72,6 +72,30 @@
     }
 
     /**
+     * Inserts a phase before the last phase in the suite. If the suite contains no phases the new
+     * phase will be inserted as the first phase.
+     */
+    public final void addBeforeLast(BasePhase<? super C> phase) {
+        ListIterator<BasePhase<? super C>> last = findLastPhase();
+        if (last.hasPrevious()) {
+            last.previous();
+        }
+        last.add(phase);
+    }
+
+    /**
+     * Returns a {@link ListIterator} at the position of the last phase in the suite. If the suite
+     * has no phases then it will return an empty iterator.
+     */
+    private ListIterator<BasePhase<? super C>> findLastPhase() {
+        ListIterator<BasePhase<? super C>> it = phases.listIterator();
+        while (it.hasNext()) {
+            it.next();
+        }
+        return it;
+    }
+
+    /**
      * Returns a {@link ListIterator} at the position of the first phase which is an instance of
      * {@code phaseClass} or null if no such phase can be found.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/NodeCostUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/NodeCostUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,8 +27,8 @@
 import java.util.function.Function;
 
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.VerificationError;
 import org.graalvm.compiler.nodes.FixedNode;
@@ -41,12 +41,12 @@
 
 public class NodeCostUtil {
 
-    private static final DebugCounter sizeComputationCount = Debug.counter("GraphCostComputationCount_Size");
-    private static final DebugCounter sizeVerificationCount = Debug.counter("GraphCostVerificationCount_Size");
+    private static final CounterKey sizeComputationCount = DebugContext.counter("GraphCostComputationCount_Size");
+    private static final CounterKey sizeVerificationCount = DebugContext.counter("GraphCostVerificationCount_Size");
 
     @SuppressWarnings("try")
     public static int computeGraphSize(StructuredGraph graph) {
-        sizeComputationCount.increment();
+        sizeComputationCount.increment(graph.getDebug());
         int size = 0;
         for (Node n : graph.getNodes()) {
             size += n.estimatedNodeSize().value;
@@ -77,14 +77,15 @@
             blockToNodes = b -> nodes.get(b);
         }
         double weightedCycles = 0D;
-        try (Debug.Scope s = Debug.scope("NodeCostSummary")) {
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("NodeCostSummary")) {
             for (Block block : cfg.getBlocks()) {
                 for (Node n : blockToNodes.apply(block)) {
                     double probWeighted = n.estimatedNodeCycles().value * block.probability();
                     assert Double.isFinite(probWeighted);
                     weightedCycles += probWeighted;
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("Node %s contributes cycles:%f size:%d to graph %s [block prob:%f]", n, n.estimatedNodeCycles().value * block.probability(),
+                    if (debug.isLogEnabled()) {
+                        debug.log("Node %s contributes cycles:%f size:%d to graph %s [block prob:%f]", n, n.estimatedNodeCycles().value * block.probability(),
                                         n.estimatedNodeSize().value, graph, block.probability());
                     }
                 }
@@ -111,7 +112,7 @@
     private static final double DELTA = 0.001D;
 
     public static void phaseFulfillsSizeContract(StructuredGraph graph, int codeSizeBefore, int codeSizeAfter, PhaseSizeContract contract) {
-        sizeVerificationCount.increment();
+        sizeVerificationCount.increment(graph.getDebug());
         final double codeSizeIncrease = contract.codeSizeIncrease();
         final double graphSizeDelta = codeSizeBefore * DELTA;
         if (deltaCompare(codeSizeAfter, codeSizeBefore * codeSizeIncrease, graphSizeDelta) > 0) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,8 +24,8 @@
 
 import java.util.function.ToDoubleFunction;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
@@ -36,15 +36,15 @@
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.StartNode;
+import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
-import org.graalvm.util.EconomicMap;
 
 /**
  * Compute probabilities for fixed nodes on the fly and cache them at {@link AbstractBeginNode}s.
  */
 public class FixedNodeProbabilityCache implements ToDoubleFunction<FixedNode> {
 
-    private static final DebugCounter computeNodeProbabilityCounter = Debug.counter("ComputeNodeProbability");
+    private static final CounterKey computeNodeProbabilityCounter = DebugContext.counter("ComputeNodeProbability");
 
     private final EconomicMap<FixedNode, Double> cache = EconomicMap.create(Equivalence.IDENTITY);
 
@@ -82,7 +82,7 @@
     @Override
     public double applyAsDouble(FixedNode node) {
         assert node != null;
-        computeNodeProbabilityCounter.increment();
+        computeNodeProbabilityCounter.increment(node.getDebug());
 
         FixedNode current = findBegin(node);
         if (current == null) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/MemoryScheduleVerification.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/MemoryScheduleVerification.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,7 +26,7 @@
 
 import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
@@ -40,9 +40,9 @@
 import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator;
 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
+import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
 import org.graalvm.word.LocationIdentity;
-import org.graalvm.util.EconomicSet;
 
 public final class MemoryScheduleVerification extends BlockIteratorClosure<EconomicSet<FloatingReadNode>> {
 
@@ -123,7 +123,7 @@
         for (FloatingReadNode r : cloneState(currentState)) {
             if (r.getLocationIdentity().overlaps(location)) {
                 // This read is killed by this location.
-                Debug.log(Debug.VERBOSE_LEVEL, "%s removing %s from state", n, r);
+                r.getDebug().log(DebugContext.VERBOSE_LEVEL, "%s removing %s from state", n, r);
                 currentState.remove(r);
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,12 +22,19 @@
  */
 package org.graalvm.compiler.phases.schedule;
 
+import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops;
+import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Formatter;
+import java.util.List;
+
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.SuppressFBWarnings;
 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.debug.Assertions;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Graph.NodeEvent;
 import org.graalvm.compiler.graph.Graph.NodeEventListener;
 import org.graalvm.compiler.graph.Graph.NodeEventScope;
@@ -69,14 +76,6 @@
 import org.graalvm.compiler.phases.Phase;
 import org.graalvm.word.LocationIdentity;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Formatter;
-import java.util.List;
-
-import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops;
-import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates;
-
 public final class SchedulePhase extends Phase {
 
     public enum SchedulingStrategy {
@@ -1014,7 +1013,7 @@
             } else if (n instanceof GuardNode) {
                 buf.format(", anchor: %s", ((GuardNode) n).getAnchor());
             }
-            Debug.log("%s", buf);
+            n.getDebug().log("%s", buf);
         }
 
         public ControlFlowGraph getCFG() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/GraphOrder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/GraphOrder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,7 +26,6 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Node;
@@ -290,7 +289,7 @@
             ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock());
 
         } catch (Throwable t) {
-            Debug.handle(t);
+            graph.getDebug().handle(t);
         }
         return true;
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,12 +22,7 @@
  */
 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.ENABLED_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 static org.graalvm.compiler.debug.DebugContext.BASIC_LEVEL;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -36,8 +31,7 @@
 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.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
@@ -59,16 +53,17 @@
 import jdk.vm.ci.meta.ResolvedJavaType;
 
 /**
- * Verifies that call sites calling one of the methods in {@link Debug} use them correctly. Correct
- * usage of the methods in {@link Debug} requires call sites to not eagerly evaluate their
- * arguments. Additionally this phase verifies that no argument is the result of a call to
+ * Verifies that call sites calling one of the methods in {@link DebugContext} use them correctly.
+ * Correct usage of the methods in {@link DebugContext} requires call sites to not eagerly evaluate
+ * their arguments. Additionally this phase verifies that no argument is the result of a call to
  * {@link StringBuilder#toString()} or {@link StringBuffer#toString()}. Ideally the parameters at
- * call sites of {@link Debug} are eliminated, and do not produce additional allocations, if
- * {@link Debug#isDumpEnabled(int)} (or {@link Debug#isLogEnabled(int)}, ...) is {@code false}.
+ * call sites of {@link DebugContext} are eliminated, and do not produce additional allocations, if
+ * {@link DebugContext#isDumpEnabled(int)} (or {@link DebugContext#isLogEnabled(int)}, ...) is
+ * {@code false}.
  *
- * Methods in {@link Debug} checked by this phase are various different versions of
- * {@link Debug#log(String)} , {@link Debug#dump(int, Object, String)},
- * {@link Debug#logAndIndent(String)} and {@link Debug#verify(Object, String)}.
+ * Methods in {@link DebugContext} checked by this phase are various different versions of
+ * {@link DebugContext#log(String)} , {@link DebugContext#dump(int, Object, String)},
+ * {@link DebugContext#logAndIndent(String)} and {@link DebugContext#verify(Object, String)}.
  */
 public class VerifyDebugUsage extends VerifyPhase<PhaseContext> {
 
@@ -82,10 +77,9 @@
     @Override
     protected boolean verify(StructuredGraph graph, PhaseContext context) {
         metaAccess = context.getMetaAccess();
-        ResolvedJavaType debugType = metaAccess.lookupJavaType(Debug.class);
+        ResolvedJavaType debugType = metaAccess.lookupJavaType(DebugContext.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)) {
@@ -102,11 +96,6 @@
                     verifyParameters(t, graph, t.arguments(), stringType, 1);
                 }
             }
-            if (callee.getDeclaringClass().equals(debugMethodMetricsType)) {
-                if (calleeName.equals("addToMetric") || calleeName.equals("getCurrentMetricValue") || calleeName.equals("incrementMetric")) {
-                    verifyParameters(t, graph, t.arguments(), stringType, 1);
-                }
-            }
             if (callee.getDeclaringClass().isAssignableFrom(graalErrorType) && !graph.method().getDeclaringClass().isAssignableFrom(graalErrorType)) {
                 if (calleeName.equals("guarantee")) {
                     verifyParameters(t, graph, t.arguments(), stringType, 0);
@@ -137,16 +126,17 @@
         }
     }
 
-    private static final Set<Integer> DebugLevels = new HashSet<>(Arrays.asList(ENABLED_LEVEL, BASIC_LEVEL, INFO_LEVEL, VERBOSE_LEVEL, DETAILED_LEVEL, VERY_DETAILED_LEVEL));
+    private static final Set<Integer> DebugLevels = new HashSet<>(
+                    Arrays.asList(DebugContext.ENABLED_LEVEL, BASIC_LEVEL, DebugContext.INFO_LEVEL, DebugContext.VERBOSE_LEVEL, DebugContext.DETAILED_LEVEL, DebugContext.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.
+     * parameter bound to {@link DebugContext#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.
+     * outlined by {@link DebugContext#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",
@@ -161,12 +151,12 @@
 
     /**
      * 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.
+     * parameter bound to {@link DebugContext#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.
+     * outlined by {@link DebugContext#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",
@@ -202,13 +192,13 @@
                     }
                 }
             }
-            if (i == 0) {
+            if (i == 1) {
                 if (verifiedCallee.getName().equals("dump")) {
                     dumpLevel = verifyDumpLevelParameter(callerGraph, debugCallTarget, verifiedCallee, arg);
                 }
-            } else if (i == 1) {
+            } else if (i == 2) {
                 if (dumpLevel != null) {
-                    verifyDumpObjectParameter(callerGraph, debugCallTarget, args, verifiedCallee, dumpLevel);
+                    verifyDumpObjectParameter(callerGraph, debugCallTarget, arg, verifiedCallee, dumpLevel);
                 }
             }
             if (varArgsIndex >= 0 && i >= varArgsIndex) {
@@ -233,38 +223,38 @@
             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)"));
+                                "In %s: parameter 0 of call to %s does not match a Debug.*_LEVEL constant: %s.%n", e, verifiedCallee.format("%H.%n(%p)"), dumpLevel);
             }
             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)"));
+                        "In %s: parameter 0 of call to %s must be a constant, not %s.%n", e, verifiedCallee.format("%H.%n(%p)"), arg);
     }
 
-    protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, List<? extends ValueNode> args, ResolvedJavaMethod verifiedCallee, Integer dumpLevel)
+    protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, ValueNode arg, ResolvedJavaMethod verifiedCallee, Integer dumpLevel)
                     throws org.graalvm.compiler.phases.VerifyPhase.VerificationError {
-        ResolvedJavaType arg1Type = ((ObjectStamp) args.get(1).stamp()).type();
-        if (metaAccess.lookupJavaType(Graph.class).isAssignableFrom(arg1Type)) {
+        ResolvedJavaType argType = ((ObjectStamp) arg.stamp()).type();
+        if (metaAccess.lookupJavaType(Graph.class).isAssignableFrom(argType)) {
             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.
+     * Verifies that dumping a {@link StructuredGraph} at level {@link DebugContext#BASIC_LEVEL} or
+     * {@link DebugContext#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) {
+        if (dumpLevel == DebugContext.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)"),
+                                "In %s: call to %s with level == DebugContext.BASIC_LEVEL not in %s.BasicLevelDumpWhitelist.%n", e, verifiedCallee.format("%H.%n(%p)"),
                                 getClass().getName());
             }
-        } else if (dumpLevel == Debug.INFO_LEVEL) {
+        } else if (dumpLevel == DebugContext.INFO_LEVEL) {
             StackTraceElement e = callerGraph.method().asStackTraceElement(debugCallTarget.invoke().bci());
             String qualifiedMethod = e.getClassName() + "." + e.getMethodName();
             if (!InfoLevelStructuredGraphDumpWhitelist.contains(qualifiedMethod)) {
@@ -288,7 +278,7 @@
                                 verifiedCallee.format("%H.%n(%p)"));
             } else {
                 throw new VerificationError(
-                                "In %s: parameter %d of call to %s appears to be a String concatenation expression.%n", e, argIdx, verifiedCallee.format("%H.%n(%p)"));
+                                "In %s: parameter %d of call to %s appears to be a String concatenation expression.", e, argIdx, verifiedCallee.format("%H.%n(%p)"));
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -40,8 +40,8 @@
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.bytecode.Bytecode;
 import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.graph.CachedGraph;
 import org.graalvm.compiler.graph.Edges;
 import org.graalvm.compiler.graph.Graph;
@@ -59,12 +59,10 @@
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
-import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.VirtualState;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
-import org.graalvm.compiler.phases.schedule.SchedulePhase;
 
 import jdk.vm.ci.meta.JavaType;
 import jdk.vm.ci.meta.ResolvedJavaField;
@@ -152,78 +150,57 @@
     private final ConstantPool constantPool;
     private final ByteBuffer buffer;
     private final WritableByteChannel channel;
-    private SnippetReflectionProvider snippetReflection;
+    private final SnippetReflectionProvider snippetReflection;
 
     private static final Charset utf8 = Charset.forName("UTF-8");
 
-    public BinaryGraphPrinter(WritableByteChannel channel) throws IOException {
+    public BinaryGraphPrinter(WritableByteChannel channel, SnippetReflectionProvider snippetReflection) throws IOException {
         constantPool = new ConstantPool();
+        this.snippetReflection = snippetReflection;
         buffer = ByteBuffer.allocateDirect(256 * 1024);
         this.channel = channel;
         writeVersion();
     }
 
     @Override
-    public void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection) {
-        this.snippetReflection = snippetReflection;
-    }
-
-    @Override
     public SnippetReflectionProvider getSnippetReflectionProvider() {
         return snippetReflection;
     }
 
     @SuppressWarnings("all")
     @Override
-    public void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
+    public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
         writeByte(BEGIN_GRAPH);
         if (CURRENT_MAJOR_VERSION >= 3) {
             writeInt(id);
             writeString(format);
             writeInt(args.length);
             for (Object a : args) {
-                writePropertyObject(a);
+                writePropertyObject(debug, a);
             }
         } else {
-            writePoolObject(formatTitle(id, format, args));
+            writePoolObject(id + ": " + String.format(format, simplifyClassArgs(args)));
         }
-        writeGraph(graph, properties);
+        writeGraph(debug, graph, properties);
         flush();
     }
 
-    private void writeGraph(Graph graph, Map<Object, Object> properties) throws IOException {
-        ScheduleResult scheduleResult = null;
-        if (graph instanceof StructuredGraph) {
-
-            StructuredGraph structuredGraph = (StructuredGraph) graph;
-            scheduleResult = structuredGraph.getLastSchedule();
-            if (scheduleResult == null) {
-
-                // Also provide a schedule when an error occurs
-                if (Options.PrintGraphWithSchedule.getValue(graph.getOptions()) || Debug.contextLookup(Throwable.class) != null) {
-                    try {
-                        SchedulePhase schedule = new SchedulePhase(graph.getOptions());
-                        schedule.apply(structuredGraph);
-                        scheduleResult = structuredGraph.getLastSchedule();
-                    } catch (Throwable t) {
-                    }
-                }
-
-            }
-        }
-        ControlFlowGraph cfg = scheduleResult == null ? Debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
+    private void writeGraph(DebugContext debug, Graph graph, Map<Object, Object> properties) throws IOException {
+        boolean needSchedule = DebugOptions.PrintGraphWithSchedule.getValue(graph.getOptions()) || debug.contextLookup(Throwable.class) != null;
+        ScheduleResult scheduleResult = needSchedule ? GraphPrinter.getScheduleOrNull(graph) : null;
+        ControlFlowGraph cfg = scheduleResult == null ? debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
         BlockMap<List<Node>> blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap();
         NodeMap<Block> nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap();
         List<Block> blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks());
-        writeProperties(properties);
-        writeNodes(graph, nodeToBlocks, cfg);
+        writeProperties(debug, properties);
+        writeNodes(debug, graph, nodeToBlocks, cfg);
         writeBlocks(blocks, blockToNodes);
     }
 
     private void flush() throws IOException {
         buffer.flip();
         /*
-         * Try not to let interrupted threads aborting the write. There's still a race here but an
+         * Try not to let interrupted threads abort the write. There's still a race here but an
          * interrupt that's been pending for a long time shouldn't stop this writing.
          */
         boolean interrupted = Thread.interrupted();
@@ -438,8 +415,11 @@
             writeInt(bci);
             StackTraceElement ste = method.asStackTraceElement(bci);
             if (ste != null) {
-                writePoolObject(ste.getFileName());
-                writeInt(ste.getLineNumber());
+                String fn = ste.getFileName();
+                writePoolObject(fn);
+                if (fn != null) {
+                    writeInt(ste.getLineNumber());
+                }
             } else {
                 writePoolObject(null);
             }
@@ -462,7 +442,7 @@
         }
     }
 
-    private void writePropertyObject(Object obj) throws IOException {
+    private void writePropertyObject(DebugContext debug, Object obj) throws IOException {
         if (obj instanceof Integer) {
             writeByte(PROPERTY_INT);
             writeInt(((Integer) obj).intValue());
@@ -483,10 +463,10 @@
             }
         } else if (obj instanceof Graph) {
             writeByte(PROPERTY_SUBGRAPH);
-            writeGraph((Graph) obj, null);
+            writeGraph(debug, (Graph) obj, null);
         } else if (obj instanceof CachedGraph) {
             writeByte(PROPERTY_SUBGRAPH);
-            writeGraph(((CachedGraph<?>) obj).getReadonlyCopy(), null);
+            writeGraph(debug, ((CachedGraph<?>) obj).getReadonlyCopy(), null);
         } else if (obj != null && obj.getClass().isArray()) {
             Class<?> componentType = obj.getClass().getComponentType();
             if (componentType.isPrimitive()) {
@@ -536,7 +516,7 @@
         return null;
     }
 
-    private void writeNodes(Graph graph, NodeMap<Block> nodeToBlocks, ControlFlowGraph cfg) throws IOException {
+    private void writeNodes(DebugContext debug, Graph graph, NodeMap<Block> nodeToBlocks, ControlFlowGraph cfg) throws IOException {
         Map<Object, Object> props = new HashMap<>();
 
         writeInt(graph.getNodeCount());
@@ -544,7 +524,7 @@
         for (Node node : graph.getNodes()) {
             NodeClass<?> nodeClass = node.getNodeClass();
             node.getDebugProperties(props);
-            if (cfg != null && Options.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
+            if (cfg != null && DebugOptions.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
                 try {
                     props.put("probability", cfg.blockFor(node).probability());
                 } catch (Throwable t) {
@@ -596,7 +576,7 @@
             writeInt(getNodeId(node));
             writePoolObject(nodeClass);
             writeByte(node.predecessor() == null ? 0 : 1);
-            writeProperties(props);
+            writeProperties(debug, props);
             writeEdges(node, Inputs);
             writeEdges(node, Successors);
 
@@ -604,7 +584,7 @@
         }
     }
 
-    private void writeProperties(Map<Object, Object> props) throws IOException {
+    private void writeProperties(DebugContext debug, Map<Object, Object> props) throws IOException {
         if (props == null) {
             writeShort((char) 0);
             return;
@@ -614,7 +594,7 @@
         for (Entry<Object, Object> entry : props.entrySet()) {
             String key = entry.getKey().toString();
             writePoolObject(key);
-            writePropertyObject(entry.getValue());
+            writePropertyObject(debug, entry.getValue());
         }
     }
 
@@ -690,13 +670,13 @@
     }
 
     @Override
-    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
+    public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
         writeByte(BEGIN_GROUP);
         writePoolObject(name);
         writePoolObject(shortName);
         writePoolObject(method);
         writeInt(bci);
-        writeProperties(properties);
+        writeProperties(debug, properties);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,12 +22,14 @@
  */
 package org.graalvm.compiler.printer;
 
+import static org.graalvm.compiler.debug.DebugOptions.PrintCFG;
+import static org.graalvm.compiler.printer.GraalDebugHandlersFactory.createDumpPath;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.OutputStream;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -39,13 +41,11 @@
 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
 import org.graalvm.compiler.core.gen.NodeLIRBuilder;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpHandler;
 import org.graalvm.compiler.debug.DebugDumpScope;
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.java.BciBlockMapping;
 import org.graalvm.compiler.lir.LIR;
@@ -55,7 +55,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
-import org.graalvm.compiler.options.UniquePathUtilities;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.serviceprovider.GraalServices;
 
 import jdk.vm.ci.code.CodeCacheProvider;
@@ -73,17 +73,12 @@
     private File cfgFile;
     private JavaMethod curMethod;
     private List<String> curDecorators = Collections.emptyList();
-    private final boolean dumpFrontend;
-
-    public CFGPrinterObserver(boolean dumpFrontend) {
-        this.dumpFrontend = dumpFrontend;
-    }
 
     @Override
-    public void dump(Object object, String format, Object... arguments) {
+    public void dump(DebugContext debug, Object object, String format, Object... arguments) {
         String message = String.format(format, arguments);
         try {
-            dumpSandboxed(object, message);
+            dumpSandboxed(debug, object, message);
         } catch (Throwable ex) {
             TTY.println("CFGPrinter: Exception during output of " + message + ": " + ex);
             ex.printStackTrace();
@@ -95,10 +90,10 @@
      * debug scope and opens a new compilation scope if this pair does not match the current method
      * and decorator pair.
      */
-    private boolean checkMethodScope() {
+    private boolean checkMethodScope(DebugContext debug) {
         JavaMethod method = null;
         ArrayList<String> decorators = new ArrayList<>();
-        for (Object o : Debug.context()) {
+        for (Object o : debug.context()) {
             if (o instanceof JavaMethod) {
                 method = (JavaMethod) o;
                 decorators.clear();
@@ -122,7 +117,6 @@
 
         if (!method.equals(curMethod) || !curDecorators.equals(decorators)) {
             cfgPrinter.printCompilation(method);
-            TTY.println("CFGPrinter: Dumping method %s to %s", method, cfgFile.getAbsolutePath());
         }
         curMethod = method;
         curDecorators = decorators;
@@ -136,30 +130,25 @@
     private LIR lastLIR = null;
     private IntervalDumper delayedIntervals = null;
 
-    public void dumpSandboxed(Object object, String message) {
+    public void dumpSandboxed(DebugContext debug, Object object, String message) {
+        OptionValues options = debug.getOptions();
+        boolean dumpFrontend = PrintCFG.getValue(options);
         if (!dumpFrontend && isFrontendObject(object)) {
             return;
         }
 
         if (cfgPrinter == null) {
-            cfgFile = getCFGPath().toFile();
             try {
-                /*
-                 * Initializing a debug environment multiple times by calling
-                 * DebugEnvironment#initialize will create new CFGPrinterObserver objects that refer
-                 * to the same file path. This means the CFG file may be overridden by another
-                 * instance. Appending to an existing CFG file is not an option as the writing
-                 * happens buffered.
-                 */
+                Graph graph = debug.contextLookupTopdown(Graph.class);
+                cfgFile = createDumpPath(options, graph, "cfg", false).toFile();
                 OutputStream out = new BufferedOutputStream(new FileOutputStream(cfgFile));
                 cfgPrinter = new CFGPrinter(out);
-            } catch (FileNotFoundException e) {
-                throw new GraalError("Could not open " + cfgFile.getAbsolutePath());
+            } catch (IOException e) {
+                throw (GraalError) new GraalError("Could not open %s", cfgFile.getAbsolutePath()).initCause(e);
             }
-            TTY.println("CFGPrinter: Output to file %s", cfgFile.getAbsolutePath());
         }
 
-        if (!checkMethodScope()) {
+        if (!checkMethodScope(debug)) {
             return;
         }
         try {
@@ -170,11 +159,11 @@
             if (object instanceof LIR) {
                 cfgPrinter.lir = (LIR) object;
             } else {
-                cfgPrinter.lir = Debug.contextLookup(LIR.class);
+                cfgPrinter.lir = debug.contextLookup(LIR.class);
             }
-            cfgPrinter.nodeLirGenerator = Debug.contextLookup(NodeLIRBuilder.class);
-            cfgPrinter.livenessInfo = Debug.contextLookup(GlobalLivenessInfo.class);
-            cfgPrinter.res = Debug.contextLookup(LIRGenerationResult.class);
+            cfgPrinter.nodeLirGenerator = debug.contextLookup(NodeLIRBuilder.class);
+            cfgPrinter.livenessInfo = debug.contextLookup(GlobalLivenessInfo.class);
+            cfgPrinter.res = debug.contextLookup(LIRGenerationResult.class);
             if (cfgPrinter.nodeLirGenerator != null) {
                 cfgPrinter.target = cfgPrinter.nodeLirGenerator.getLIRGeneratorTool().target();
             }
@@ -182,7 +171,7 @@
                 cfgPrinter.cfg = (ControlFlowGraph) cfgPrinter.lir.getControlFlowGraph();
             }
 
-            CodeCacheProvider codeCache = Debug.contextLookup(CodeCacheProvider.class);
+            CodeCacheProvider codeCache = debug.contextLookup(CodeCacheProvider.class);
             if (codeCache != null) {
                 cfgPrinter.target = codeCache.getTarget();
             }
@@ -217,7 +206,7 @@
                 final CompilationResult compResult = (CompilationResult) object;
                 cfgPrinter.printMachineCode(disassemble(codeCache, compResult, null), message);
             } else if (object instanceof InstalledCode) {
-                CompilationResult compResult = Debug.contextLookup(CompilationResult.class);
+                CompilationResult compResult = debug.contextLookup(CompilationResult.class);
                 if (compResult != null) {
                     cfgPrinter.printMachineCode(disassemble(codeCache, compResult, (InstalledCode) object), message);
                 }
@@ -226,7 +215,7 @@
                     cfgPrinter.printIntervals(message, (IntervalDumper) object);
                 } else {
                     if (delayedIntervals != null) {
-                        Debug.log("Some delayed intervals were dropped (%s)", delayedIntervals);
+                        debug.log("Some delayed intervals were dropped (%s)", delayedIntervals);
                     }
                     delayedIntervals = (IntervalDumper) object;
                 }
@@ -248,10 +237,6 @@
         }
     }
 
-    private static Path getCFGPath() {
-        return UniquePathUtilities.getPath(DebugScope.getConfig().getOptions(), Options.PrintCFGFileName, Options.DumpPath, "cfg");
-    }
-
     /** Lazy initialization to delay service lookup until disassembler is actually needed. */
     static class DisassemblerHolder {
         private static final DisassemblerProvider disassembler;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,10 +22,11 @@
  */
 package org.graalvm.compiler.printer;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsCheckConstants;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsExcludeVirtuals;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsRemoveIdentities;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintCanonicalGraphStringFlavor;
+import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsCheckConstants;
+import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsExcludeVirtuals;
+import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsRemoveIdentities;
+import static org.graalvm.compiler.debug.DebugOptions.PrintCanonicalGraphStringFlavor;
+import static org.graalvm.compiler.printer.GraalDebugHandlersFactory.sanitizedFileName;
 
 import java.io.BufferedWriter;
 import java.io.FileWriter;
@@ -42,7 +43,7 @@
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.core.common.Fields;
-import org.graalvm.compiler.debug.TTY;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
@@ -61,23 +62,14 @@
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.schedule.SchedulePhase;
 
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 public class CanonicalStringGraphPrinter implements GraphPrinter {
     private static final Pattern IDENTITY_PATTERN = Pattern.compile("([A-Za-z0-9$_]+)@[0-9a-f]+");
-    private Path currentDirectory;
-    private Path root;
-    private SnippetReflectionProvider snippetReflection;
+    private final SnippetReflectionProvider snippetReflection;
 
-    public CanonicalStringGraphPrinter(Path directory) {
-        this.currentDirectory = directory;
-        this.root = directory;
-    }
-
-    @Override
-    public void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection) {
+    public CanonicalStringGraphPrinter(SnippetReflectionProvider snippetReflection) {
         this.snippetReflection = snippetReflection;
     }
 
@@ -86,20 +78,6 @@
         return snippetReflection;
     }
 
-    protected static String escapeFileName(String name) {
-        byte[] bytes = name.getBytes();
-        StringBuilder sb = new StringBuilder();
-        for (byte b : bytes) {
-            if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == '-' || b == '_') {
-                sb.append((char) b);
-            } else {
-                sb.append('%');
-                sb.append(Integer.toHexString(b));
-            }
-        }
-        return sb.toString();
-    }
-
     private static String removeIdentities(String str) {
         return IDENTITY_PATTERN.matcher(str).replaceAll("$1");
     }
@@ -144,99 +122,122 @@
     }
 
     protected static void writeCanonicalExpressionCFGString(StructuredGraph graph, boolean checkConstants, boolean removeIdentities, PrintWriter writer) {
-        ControlFlowGraph controlFlowGraph = ControlFlowGraph.compute(graph, true, true, false, false);
-        for (Block block : controlFlowGraph.getBlocks()) {
-            writer.print("Block ");
-            writer.print(block);
-            writer.print(" ");
-            if (block == controlFlowGraph.getStartBlock()) {
-                writer.print("* ");
-            }
-            writer.print("-> ");
-            for (Block successor : block.getSuccessors()) {
-                writer.print(successor);
+        ControlFlowGraph controlFlowGraph = getControlFlowGraph(graph);
+        if (controlFlowGraph == null) {
+            return;
+        }
+        try {
+            for (Block block : controlFlowGraph.getBlocks()) {
+                writer.print("Block ");
+                writer.print(block);
                 writer.print(" ");
-            }
-            writer.println();
-            FixedNode node = block.getBeginNode();
-            while (node != null) {
-                writeCanonicalGraphExpressionString(node, checkConstants, removeIdentities, writer);
+                if (block == controlFlowGraph.getStartBlock()) {
+                    writer.print("* ");
+                }
+                writer.print("-> ");
+                for (Block successor : block.getSuccessors()) {
+                    writer.print(successor);
+                    writer.print(" ");
+                }
                 writer.println();
-                if (node instanceof FixedWithNextNode) {
-                    node = ((FixedWithNextNode) node).next();
-                } else {
-                    node = null;
+                FixedNode node = block.getBeginNode();
+                while (node != null) {
+                    writeCanonicalGraphExpressionString(node, checkConstants, removeIdentities, writer);
+                    writer.println();
+                    if (node instanceof FixedWithNextNode) {
+                        node = ((FixedWithNextNode) node).next();
+                    } else {
+                        node = null;
+                    }
                 }
             }
+        } catch (Throwable e) {
+            writer.println();
+            e.printStackTrace(writer);
+        }
+    }
+
+    protected static ControlFlowGraph getControlFlowGraph(StructuredGraph graph) {
+        try {
+            return ControlFlowGraph.compute(graph, true, true, false, false);
+        } catch (Throwable e) {
+            // Ignore a non-well formed graph
+            return null;
         }
     }
 
     protected static void writeCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants, PrintWriter writer) {
-        SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST);
-        schedule.apply(graph);
-        StructuredGraph.ScheduleResult scheduleResult = graph.getLastSchedule();
-
-        NodeMap<Integer> canonicalId = graph.createNodeMap();
-        int nextId = 0;
-
-        List<String> constantsLines = null;
-        if (checkConstants) {
-            constantsLines = new ArrayList<>();
+        StructuredGraph.ScheduleResult scheduleResult = GraphPrinter.getScheduleOrNull(graph);
+        if (scheduleResult == null) {
+            return;
         }
+        try {
 
-        for (Block block : scheduleResult.getCFG().getBlocks()) {
-            writer.print("Block ");
-            writer.print(block);
-            writer.print(" ");
-            if (block == scheduleResult.getCFG().getStartBlock()) {
-                writer.print("* ");
-            }
-            writer.print("-> ");
-            for (Block successor : block.getSuccessors()) {
-                writer.print(successor);
-                writer.print(" ");
+            NodeMap<Integer> canonicalId = graph.createNodeMap();
+            int nextId = 0;
+
+            List<String> constantsLines = null;
+            if (checkConstants) {
+                constantsLines = new ArrayList<>();
             }
-            writer.println();
-            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 (node instanceof ConstantNode) {
-                            if (constantsLines != null) {
-                                String name = node.toString(Verbosity.Name);
-                                String str = name + (excludeVirtual ? "" : "    (" + filteredUsageCount(node) + ")");
-                                constantsLines.add(str);
-                            }
-                        } else {
-                            int id;
-                            if (canonicalId.get(node) != null) {
-                                id = canonicalId.get(node);
+
+            for (Block block : scheduleResult.getCFG().getBlocks()) {
+                writer.print("Block ");
+                writer.print(block);
+                writer.print(" ");
+                if (block == scheduleResult.getCFG().getStartBlock()) {
+                    writer.print("* ");
+                }
+                writer.print("-> ");
+                for (Block successor : block.getSuccessors()) {
+                    writer.print(successor);
+                    writer.print(" ");
+                }
+                writer.println();
+                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 (node instanceof ConstantNode) {
+                                if (constantsLines != null) {
+                                    String name = node.toString(Verbosity.Name);
+                                    String str = name + (excludeVirtual ? "" : "    (" + filteredUsageCount(node) + ")");
+                                    constantsLines.add(str);
+                                }
                             } else {
-                                id = nextId++;
-                                canonicalId.set(node, id);
+                                int id;
+                                if (canonicalId.get(node) != null) {
+                                    id = canonicalId.get(node);
+                                } else {
+                                    id = nextId++;
+                                    canonicalId.set(node, id);
+                                }
+                                String name = node.getClass().getSimpleName();
+                                writer.print("  ");
+                                writer.print(id);
+                                writer.print("|");
+                                writer.print(name);
+                                if (!excludeVirtual) {
+                                    writer.print("    (");
+                                    writer.print(filteredUsageCount(node));
+                                    writer.print(")");
+                                }
+                                writer.println();
                             }
-                            String name = node.getClass().getSimpleName();
-                            writer.print("  ");
-                            writer.print(id);
-                            writer.print("|");
-                            writer.print(name);
-                            if (!excludeVirtual) {
-                                writer.print("    (");
-                                writer.print(filteredUsageCount(node));
-                                writer.print(")");
-                            }
-                            writer.println();
                         }
                     }
                 }
             }
-        }
-        if (constantsLines != null) {
-            writer.print(constantsLines.size());
-            writer.println(" constants:");
-            Collections.sort(constantsLines);
-            for (String s : constantsLines) {
-                writer.println(s);
+            if (constantsLines != null) {
+                writer.print(constantsLines.size());
+                writer.println(" constants:");
+                Collections.sort(constantsLines);
+                for (String s : constantsLines) {
+                    writer.println(s);
+                }
             }
+        } catch (Throwable t) {
+            writer.println();
+            t.printStackTrace(writer);
         }
     }
 
@@ -253,22 +254,29 @@
     }
 
     @Override
-    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
-        currentDirectory = currentDirectory.resolve(escapeFileName(name));
+    public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
+    }
+
+    private StructuredGraph currentGraph;
+    private Path currentDirectory;
+
+    private Path getDirectory(StructuredGraph graph) throws IOException {
+        if (graph == currentGraph) {
+            return currentDirectory;
+        }
+        currentDirectory = GraalDebugHandlersFactory.createDumpPath(graph.getOptions(), graph, "graph-strings", true);
+        currentGraph = graph;
+        return currentDirectory;
     }
 
     @Override
-    public void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
+    public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
         if (graph instanceof StructuredGraph) {
             OptionValues options = graph.getOptions();
             StructuredGraph structuredGraph = (StructuredGraph) graph;
-            currentDirectory.toFile().mkdirs();
-            if (this.root != null) {
-                TTY.println("Dumping string graphs in %s", this.root);
-                this.root = null;
-            }
-            String title = formatTitle(id, format, args);
-            Path filePath = currentDirectory.resolve(escapeFileName(title));
+            Path outDirectory = getDirectory(structuredGraph);
+            String title = String.format("%03d-%s.txt", id, String.format(format, simplifyClassArgs(args)));
+            Path filePath = outDirectory.resolve(sanitizedFileName(title));
             try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filePath.toFile())))) {
                 switch (PrintCanonicalGraphStringFlavor.getValue(options)) {
                     case 1:
@@ -285,8 +293,6 @@
 
     @Override
     public void endGroup() throws IOException {
-        currentDirectory = currentDirectory.getParent();
-        assert currentDirectory != null;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugConfigCustomizer.java	Fri Jul 07 10:37:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.graalvm.compiler.printer;
-
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DumpPath;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintBinaryGraphPort;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintBinaryGraphs;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintCanonicalGraphStringsDirectory;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintGraphHost;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintGraphFileName;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintXmlGraphPort;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.nio.channels.ClosedByInterruptException;
-import java.nio.channels.FileChannel;
-import java.nio.channels.SocketChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfig;
-import org.graalvm.compiler.debug.DebugConfigCustomizer;
-import org.graalvm.compiler.debug.DebugDumpHandler;
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
-import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.graph.Node;
-import org.graalvm.compiler.nodeinfo.Verbosity;
-import org.graalvm.compiler.nodes.util.GraphUtil;
-import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.options.UniquePathUtilities;
-import org.graalvm.compiler.serviceprovider.ServiceProvider;
-
-@ServiceProvider(DebugConfigCustomizer.class)
-public class GraalDebugConfigCustomizer implements DebugConfigCustomizer {
-
-    @Override
-    public void customize(DebugConfig config) {
-        OptionValues options = config.getOptions();
-        if (Options.PrintGraphFile.getValue(options)) {
-            config.dumpHandlers().add(new GraphPrinterDumpHandler(() -> createFilePrinter(options)));
-        } else {
-            config.dumpHandlers().add(new GraphPrinterDumpHandler(() -> createNetworkPrinter(options)));
-        }
-        if (Options.PrintCanonicalGraphStrings.getValue(options)) {
-            config.dumpHandlers().add(new GraphPrinterDumpHandler(() -> createStringPrinter(options)));
-        }
-        config.dumpHandlers().add(new NodeDumper());
-        if (Options.PrintCFG.getValue(options) || Options.PrintBackendCFG.getValue(options)) {
-            if (Options.PrintBinaryGraphs.getValue(options) && Options.PrintCFG.getValue(options)) {
-                TTY.out.println("Complete C1Visualizer dumping slows down PrintBinaryGraphs: use -Dgraal.PrintCFG=false to disable it");
-            }
-            config.dumpHandlers().add(new CFGPrinterObserver(Options.PrintCFG.getValue(options)));
-        }
-        config.verifyHandlers().add(new NoDeadCodeVerifyHandler());
-    }
-
-    private static class NodeDumper implements DebugDumpHandler {
-        @Override
-        public void dump(Object object, String format, Object... arguments) {
-            if (object instanceof Node) {
-                String location = GraphUtil.approxSourceLocation((Node) object);
-                String node = ((Node) object).toString(Verbosity.Debugger);
-                if (location != null) {
-                    Debug.log("Context obj %s (approx. location: %s)", node, location);
-                } else {
-                    Debug.log("Context obj %s", node);
-                }
-            }
-        }
-
-        @Override
-        public void close() {
-        }
-
-        @Override
-        public void addCapability(Object capability) {
-        }
-    }
-
-    private static CanonicalStringGraphPrinter createStringPrinter(OptionValues options) {
-        // Construct the path to the directory.
-        Path path = UniquePathUtilities.getPath(options, PrintCanonicalGraphStringsDirectory, Options.DumpPath, "");
-        return new CanonicalStringGraphPrinter(path);
-    }
-
-    private static GraphPrinter createNetworkPrinter(OptionValues options) throws IOException {
-        String host = PrintGraphHost.getValue(options);
-        int port = PrintBinaryGraphs.getValue(options) ? PrintBinaryGraphPort.getValue(options) : PrintXmlGraphPort.getValue(options);
-        try {
-            GraphPrinter printer;
-            if (Options.PrintBinaryGraphs.getValue(options)) {
-                printer = new BinaryGraphPrinter(SocketChannel.open(new InetSocketAddress(host, port)));
-            } else {
-                printer = new IdealGraphPrinter(new Socket(host, port).getOutputStream(), true);
-            }
-            TTY.println("Connected to the IGV on %s:%d", host, port);
-            return printer;
-        } catch (ClosedByInterruptException | InterruptedIOException e) {
-            /*
-             * Interrupts should not count as errors because they may be caused by a cancelled Graal
-             * compilation. ClosedByInterruptException occurs if the SocketChannel could not be
-             * opened. InterruptedIOException occurs if new Socket(..) was interrupted.
-             */
-            return null;
-        } catch (IOException e) {
-            if (!Options.PrintGraphFile.hasBeenSet(options)) {
-                TTY.println(String.format("Could not connect to the IGV on %s:%d - falling back to file dumping...", host, port));
-                return createFilePrinter(options);
-            } else {
-                throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e);
-            }
-        }
-    }
-
-    private static Path getFilePrinterPath(OptionValues options) {
-        // Construct the path to the file.
-        return UniquePathUtilities.getPath(options, PrintGraphFileName, DumpPath, PrintBinaryGraphs.getValue(options) ? "bgv" : "gv.xml");
-    }
-
-    private static GraphPrinter createFilePrinter(OptionValues options) throws IOException {
-        Path path = getFilePrinterPath(options);
-        try {
-            GraphPrinter printer;
-            if (Options.PrintBinaryGraphs.getValue(options)) {
-                printer = new BinaryGraphPrinter(FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW));
-            } else {
-                printer = new IdealGraphPrinter(Files.newOutputStream(path), true);
-            }
-            TTY.println("Dumping IGV graphs to %s", path.toAbsolutePath().toString());
-            return printer;
-        } catch (IOException e) {
-            throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e);
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java	Fri Jul 07 09:40:47 2017 -0700
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.printer;
+
+import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphPort;
+import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphs;
+import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost;
+import static org.graalvm.compiler.debug.DebugOptions.PrintXmlGraphPort;
+import static org.graalvm.compiler.debug.DebugOptions.ShowDumpFiles;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpHandler;
+import org.graalvm.compiler.debug.DebugHandler;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.debug.TTY;
+import org.graalvm.compiler.debug.UniquePathUtilities;
+import org.graalvm.compiler.graph.Graph;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodeinfo.Verbosity;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.util.GraphUtil;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.serviceprovider.ServiceProvider;
+
+@ServiceProvider(DebugHandlersFactory.class)
+public class GraalDebugHandlersFactory implements DebugHandlersFactory {
+
+    private final SnippetReflectionProvider snippetReflection;
+
+    public GraalDebugHandlersFactory() {
+        this.snippetReflection = null;
+    }
+
+    public GraalDebugHandlersFactory(SnippetReflectionProvider snippetReflection) {
+        this.snippetReflection = snippetReflection;
+    }
+
+    @Override
+    public List<DebugHandler> createHandlers(OptionValues options) {
+        List<DebugHandler> handlers = new ArrayList<>();
+        if (DebugOptions.PrintGraphFile.getValue(options)) {
+            handlers.add(new GraphPrinterDumpHandler((graph) -> createFilePrinter(graph, options, snippetReflection)));
+        } else {
+            handlers.add(new GraphPrinterDumpHandler((graph) -> createNetworkPrinter(graph, options, snippetReflection)));
+        }
+        if (DebugOptions.PrintCanonicalGraphStrings.getValue(options)) {
+            handlers.add(new GraphPrinterDumpHandler((graph) -> createStringPrinter(snippetReflection)));
+        }
+        handlers.add(new NodeDumper());
+        if (DebugOptions.PrintCFG.getValue(options) || DebugOptions.PrintBackendCFG.getValue(options)) {
+            if (DebugOptions.PrintBinaryGraphs.getValue(options) && DebugOptions.PrintCFG.getValue(options)) {
+                TTY.out.println("Complete C1Visualizer dumping slows down PrintBinaryGraphs: use -Dgraal.PrintCFG=false to disable it");
+            }
+            handlers.add(new CFGPrinterObserver());
+        }
+        handlers.add(new NoDeadCodeVerifyHandler());
+        return handlers;
+    }
+
+    private static class NodeDumper implements DebugDumpHandler {
+        @Override
+        public void dump(DebugContext debug, Object object, String format, Object... arguments) {
+            if (object instanceof Node) {
+                Node node = (Node) object;
+                String location = GraphUtil.approxSourceLocation(node);
+                String nodeName = node.toString(Verbosity.Debugger);
+                if (location != null) {
+                    debug.log("Context obj %s (approx. location: %s)", nodeName, location);
+                } else {
+                    debug.log("Context obj %s", nodeName);
+                }
+            }
+        }
+    }
+
+    private static CanonicalStringGraphPrinter createStringPrinter(SnippetReflectionProvider snippetReflection) {
+        return new CanonicalStringGraphPrinter(snippetReflection);
+    }
+
+    public static String sanitizedFileName(String name) {
+        try {
+            Paths.get(name);
+            return name;
+        } catch (InvalidPathException e) {
+            // fall through
+        }
+        StringBuilder buf = new StringBuilder(name.length());
+        for (int i = 0; i < name.length(); i++) {
+            char c = name.charAt(i);
+            try {
+                Paths.get(String.valueOf(c));
+            } catch (InvalidPathException e) {
+                buf.append('_');
+            }
+            buf.append(c);
+        }
+        return buf.toString();
+    }
+
+    private static GraphPrinter createNetworkPrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException {
+        String host = PrintGraphHost.getValue(options);
+        int port = PrintBinaryGraphs.getValue(options) ? PrintBinaryGraphPort.getValue(options) : PrintXmlGraphPort.getValue(options);
+        try {
+            GraphPrinter printer;
+            if (DebugOptions.PrintBinaryGraphs.getValue(options)) {
+                printer = new BinaryGraphPrinter(SocketChannel.open(new InetSocketAddress(host, port)), snippetReflection);
+            } else {
+                printer = new IdealGraphPrinter(new Socket(host, port).getOutputStream(), true, snippetReflection);
+            }
+            TTY.println("Connected to the IGV on %s:%d", host, port);
+            return printer;
+        } catch (ClosedByInterruptException | InterruptedIOException e) {
+            /*
+             * Interrupts should not count as errors because they may be caused by a cancelled Graal
+             * compilation. ClosedByInterruptException occurs if the SocketChannel could not be
+             * opened. InterruptedIOException occurs if new Socket(..) was interrupted.
+             */
+            return null;
+        } catch (IOException e) {
+            if (!DebugOptions.PrintGraphFile.hasBeenSet(options)) {
+                return createFilePrinter(graph, options, snippetReflection);
+            } else {
+                throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e);
+            }
+        }
+    }
+
+    private static final AtomicInteger unknownCompilationId = new AtomicInteger();
+
+    /**
+     * Creates a new file or directory for dumping based on a given graph and a file extension.
+     *
+     * @param graph a base path name is derived from {@code graph}
+     * @param extension a suffix which if non-null and non-empty added to the end of the returned
+     *            path separated by a {@code "."}
+     * @param createDirectory specifies if this is a request to create a directory instead of a file
+     * @return the created directory or file
+     * @throws IOException if there was an error creating the directory or file
+     */
+    static Path createDumpPath(OptionValues options, Graph graph, String extension, boolean createDirectory) throws IOException {
+        CompilationIdentifier compilationId = CompilationIdentifier.INVALID_COMPILATION_ID;
+        String id = null;
+        String label = null;
+        if (graph instanceof StructuredGraph) {
+            StructuredGraph sgraph = (StructuredGraph) graph;
+            label = getGraphName(sgraph);
+            compilationId = sgraph.compilationId();
+            if (compilationId == CompilationIdentifier.INVALID_COMPILATION_ID) {
+                id = graph.getClass().getSimpleName() + "-" + sgraph.graphId();
+            } else {
+                id = compilationId.toString(CompilationIdentifier.Verbosity.ID);
+            }
+        } else {
+            label = graph == null ? null : graph.name != null ? graph.name : graph.toString();
+            id = "UnknownCompilation-" + unknownCompilationId.incrementAndGet();
+        }
+        String ext = UniquePathUtilities.formatExtension(extension);
+        Path result = createUnique(DebugOptions.getDumpDirectory(options), id, label, ext, createDirectory);
+        if (ShowDumpFiles.getValue(options)) {
+            TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString());
+        }
+        return result;
+    }
+
+    /**
+     * A maximum file name length supported by most file systems. There is no platform independent
+     * way to get this in Java.
+     */
+    private static final int MAX_FILE_NAME_LENGTH = 255;
+
+    private static final String ELLIPSIS = "...";
+
+    private static Path createUnique(Path dumpDir, String id, String label, String ext, boolean createDirectory) throws IOException {
+        String timestamp = "";
+        for (;;) {
+            int fileNameLengthWithoutLabel = timestamp.length() + ext.length() + id.length() + "[]".length();
+            int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel;
+            String fileName;
+            if (labelLengthLimit < ELLIPSIS.length()) {
+                // This means `id` is very long
+                String suffix = timestamp + ext;
+                int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), id.length());
+                fileName = id.substring(0, idLengthLimit) + suffix;
+            } else {
+                if (label == null) {
+                    fileName = sanitizedFileName(id + timestamp + ext);
+                } else {
+                    String adjustedLabel = label;
+                    if (label.length() > labelLengthLimit) {
+                        adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS;
+                    }
+                    fileName = sanitizedFileName(id + '[' + adjustedLabel + ']' + timestamp + ext);
+                }
+            }
+            Path result = dumpDir.resolve(fileName);
+            try {
+                if (createDirectory) {
+                    return Files.createDirectory(result);
+                } else {
+                    return Files.createFile(result);
+                }
+            } catch (FileAlreadyExistsException e) {
+                timestamp = "_" + Long.toString(System.currentTimeMillis());
+            }
+        }
+    }
+
+    private static String getGraphName(StructuredGraph graph) {
+        if (graph.name != null) {
+            return graph.name;
+        } else if (graph.method() != null) {
+            return graph.method().format("%h.%n(%p)").replace(" ", "");
+        } else {
+            return graph.toString();
+        }
+    }
+
+    private static GraphPrinter createFilePrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException {
+        Path path = createDumpPath(options, graph, PrintBinaryGraphs.getValue(options) ? "bgv" : "gv.xml", false);
+        try {
+            GraphPrinter printer;
+            if (DebugOptions.PrintBinaryGraphs.getValue(options)) {
+                printer = new BinaryGraphPrinter(FileChannel.open(path, StandardOpenOption.WRITE), snippetReflection);
+            } else {
+                printer = new IdealGraphPrinter(Files.newOutputStream(path), true, snippetReflection);
+            }
+            return printer;
+        } catch (IOException e) {
+            throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e);
+        }
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,8 +30,12 @@
 import java.util.Map;
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.serviceprovider.JDK9Method;
 
 import jdk.vm.ci.meta.JavaConstant;
@@ -48,18 +52,16 @@
      * Starts a new group of graphs with the given name, short name and method byte code index (BCI)
      * as properties.
      */
-    void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException;
+    void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException;
 
     /**
      * Prints an entire {@link Graph} with the specified title, optionally using short names for
      * nodes.
      */
-    void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException;
+    void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException;
 
     SnippetReflectionProvider getSnippetReflectionProvider();
 
-    void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection);
-
     /**
      * Ends the current group.
      */
@@ -142,20 +144,30 @@
         }
     }
 
-    default String formatTitle(int id, String format, Object... args) {
-        /*
-         * If an argument is a Class, replace it with the simple name.
-         */
-        Object[] newArgs = new Object[args.length];
-        for (int i = 0; i < newArgs.length; i++) {
+    /**
+     * Replaces all {@link JavaType} elements in {@code args} with the result of
+     * {@link JavaType#getUnqualifiedName()}.
+     *
+     * @return a copy of {@code args} with the above mentioned substitutions or {@code args} if no
+     *         substitutions were performed
+     */
+    default Object[] simplifyClassArgs(Object... args) {
+        Object[] res = args;
+        for (int i = 0; i < args.length; i++) {
             Object arg = args[i];
             if (arg instanceof JavaType) {
-                newArgs[i] = ((JavaType) arg).getUnqualifiedName();
+                if (args == res) {
+                    res = new Object[args.length];
+                    for (int a = 0; a < i; a++) {
+                        res[a] = args[a];
+                    }
+                }
+                res[i] = ((JavaType) arg).getUnqualifiedName();
             } else {
-                newArgs[i] = arg;
+                res[i] = arg;
             }
         }
-        return id + ": " + String.format(format, newArgs);
+        return res;
     }
 
     static String truncate(String s) {
@@ -198,4 +210,23 @@
         }
         return buf.append('}').toString();
     }
+
+    @SuppressWarnings("try")
+    static StructuredGraph.ScheduleResult getScheduleOrNull(Graph graph) {
+        if (graph instanceof StructuredGraph) {
+            StructuredGraph sgraph = (StructuredGraph) graph;
+            StructuredGraph.ScheduleResult scheduleResult = sgraph.getLastSchedule();
+            if (scheduleResult == null) {
+                DebugContext debug = graph.getDebug();
+                try (Scope scope = debug.disable()) {
+                    SchedulePhase schedule = new SchedulePhase(graph.getOptions());
+                    schedule.apply(sgraph);
+                    scheduleResult = sgraph.getLastSchedule();
+                } catch (Throwable t) {
+                }
+            }
+            return scheduleResult;
+        }
+        return null;
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,7 +22,7 @@
  */
 package org.graalvm.compiler.printer;
 
-import static org.graalvm.compiler.debug.GraalDebugConfig.asJavaMethod;
+import static org.graalvm.compiler.debug.DebugConfig.asJavaMethod;
 
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
@@ -36,19 +36,15 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 
-import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
-import org.graalvm.compiler.debug.DebugConfig;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpHandler;
 import org.graalvm.compiler.debug.DebugDumpScope;
-import org.graalvm.compiler.debug.GraalDebugConfig;
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
-import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.contract.NodeCostUtil;
 
 import jdk.vm.ci.meta.JavaMethod;
@@ -58,15 +54,13 @@
 
 /**
  * Observes compilation events and uses {@link IdealGraphPrinter} to generate a graph representation
- * that can be inspected with the <a href="http://kenai.com/projects/igv">Ideal Graph Visualizer</a>
- * .
+ * that can be inspected with the Graph Visualizer.
  */
 public class GraphPrinterDumpHandler implements DebugDumpHandler {
 
     private static final int FAILURE_LIMIT = 8;
     private final GraphPrinterSupplier printerSupplier;
     protected GraphPrinter printer;
-    private SnippetReflectionProvider snippetReflection;
     private List<String> previousInlineContext;
     private int[] dumpIds = {};
     private int failuresCount;
@@ -76,7 +70,7 @@
 
     @FunctionalInterface
     public interface GraphPrinterSupplier {
-        GraphPrinter get() throws IOException;
+        GraphPrinter get(Graph graph) throws IOException;
     }
 
     /**
@@ -92,20 +86,18 @@
         this.sunJavaCommand = System.getProperty("sun.java.command");
     }
 
-    private void ensureInitialized() {
+    private void ensureInitialized(Graph graph) {
         if (printer == null) {
             if (failuresCount >= FAILURE_LIMIT) {
                 return;
             }
             previousInlineContext = new ArrayList<>();
             inlineContextMap = new WeakHashMap<>();
+            DebugContext debug = graph.getDebug();
             try {
-                printer = printerSupplier.get();
-                if (snippetReflection != null) {
-                    printer.setSnippetReflectionProvider(snippetReflection);
-                }
+                printer = printerSupplier.get(graph);
             } catch (IOException e) {
-                handleException(e);
+                handleException(debug, e);
             }
         }
     }
@@ -120,13 +112,14 @@
 
     @Override
     @SuppressWarnings("try")
-    public void dump(Object object, final String format, Object... arguments) {
-        if (object instanceof Graph && Options.PrintGraph.getValue(DebugScope.getConfig().getOptions())) {
-            ensureInitialized();
+    public void dump(DebugContext debug, Object object, final String format, Object... arguments) {
+        OptionValues options = debug.getOptions();
+        if (object instanceof Graph && DebugOptions.PrintGraph.getValue(options)) {
+            final Graph graph = (Graph) object;
+            ensureInitialized(graph);
             if (printer == null) {
                 return;
             }
-            final Graph graph = (Graph) object;
 
             // Get all current JavaMethod instances in the context.
             List<String> inlineContext = getInlineContext(graph);
@@ -135,21 +128,20 @@
                 Map<Object, Object> properties = new HashMap<>();
                 properties.put("graph", graph.toString());
                 addCompilationId(properties, graph);
-                addCFGFileName(properties);
                 if (inlineContext.equals(previousInlineContext)) {
                     /*
                      * two different graphs have the same inline context, so make sure they appear
                      * in different folders by closing and reopening the top scope.
                      */
                     int inlineDepth = previousInlineContext.size() - 1;
-                    closeScope(inlineDepth);
-                    openScope(inlineContext.get(inlineDepth), inlineDepth, properties);
+                    closeScope(debug, inlineDepth);
+                    openScope(debug, inlineContext.get(inlineDepth), inlineDepth, properties);
                 } else {
                     // Check for method scopes that must be closed since the previous dump.
                     for (int i = 0; i < previousInlineContext.size(); ++i) {
                         if (i >= inlineContext.size() || !inlineContext.get(i).equals(previousInlineContext.get(i))) {
                             for (int inlineDepth = previousInlineContext.size() - 1; inlineDepth >= i; --inlineDepth) {
-                                closeScope(inlineDepth);
+                                closeScope(debug, inlineDepth);
                             }
                             break;
                         }
@@ -158,7 +150,7 @@
                     for (int i = 0; i < inlineContext.size(); ++i) {
                         if (i >= previousInlineContext.size() || !inlineContext.get(i).equals(previousInlineContext.get(i))) {
                             for (int inlineDepth = i; inlineDepth < inlineContext.size(); ++inlineDepth) {
-                                openScope(inlineContext.get(inlineDepth), inlineDepth, inlineDepth == inlineContext.size() - 1 ? properties : null);
+                                openScope(debug, inlineContext.get(inlineDepth), inlineDepth, inlineDepth == inlineContext.size() - 1 ? properties : null);
                             }
                             break;
                         }
@@ -169,11 +161,11 @@
             // Save inline context for next dump.
             previousInlineContext = inlineContext;
 
-            try (Scope s = Debug.sandbox("PrintingGraph", null)) {
+            try (DebugContext.Scope s = debug.sandbox("PrintingGraph", null)) {
                 // Finally, output the graph.
                 Map<Object, Object> properties = new HashMap<>();
                 properties.put("graph", graph.toString());
-                properties.put("scope", Debug.currentScope());
+                properties.put("scope", debug.getCurrentScopeName());
                 if (graph instanceof StructuredGraph) {
                     properties.put("compilationIdentifier", ((StructuredGraph) graph).compilationId());
                     try {
@@ -183,18 +175,17 @@
                         properties.put("node-cost-exception", t.getMessage());
                     }
                 }
-                addCFGFileName(properties);
-                printer.print(graph, properties, nextDumpId(), format, arguments);
+                printer.print(debug, graph, properties, nextDumpId(), format, arguments);
             } catch (IOException e) {
-                handleException(e);
+                handleException(debug, e);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
     }
 
-    void handleException(IOException e) {
-        if (GraalDebugConfig.Options.DumpingErrorsAreFatal.getValue(DebugScope.getConfig().getOptions())) {
+    void handleException(DebugContext debug, IOException e) {
+        if (debug != null && DebugOptions.DumpingErrorsAreFatal.getValue(debug.getOptions())) {
             throw new GraalError(e);
         }
         if (e instanceof ClosedByInterruptException) {
@@ -206,11 +197,9 @@
             failuresCount++;
         }
         printer = null;
+        e.printStackTrace(TTY.out);
         if (failuresCount > FAILURE_LIMIT) {
-            e.printStackTrace(TTY.out);
-            TTY.println("Too many failures with dumping.  Disabling dump in thread " + Thread.currentThread());
-        } else {
-            TTY.println(e.getMessage());
+            TTY.println("Too many failures with dumping. Disabling dump in thread " + Thread.currentThread());
         }
     }
 
@@ -220,29 +209,14 @@
         }
     }
 
-    private static void addCFGFileName(Map<Object, Object> properties) {
-        DebugConfig config = DebugScope.getConfig();
-        if (config != null) {
-            for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
-                if (dumpHandler instanceof CFGPrinterObserver) {
-                    CFGPrinterObserver cfg = (CFGPrinterObserver) dumpHandler;
-                    String path = cfg.getDumpPath();
-                    if (path != null) {
-                        properties.put("PrintCFGFileName", path);
-                    }
-                    return;
-                }
-            }
-        }
-    }
-
     private List<String> getInlineContext(Graph graph) {
         List<String> result = inlineContextMap.get(graph);
         if (result == null) {
             result = new ArrayList<>();
             Object lastMethodOrGraph = null;
             boolean graphSeen = false;
-            for (Object o : Debug.context()) {
+            DebugContext debug = graph.getDebug();
+            for (Object o : debug.context()) {
                 if (o == graph) {
                     graphSeen = true;
                 }
@@ -307,7 +281,7 @@
         }
     }
 
-    private void openScope(String name, int inlineDepth, Map<Object, Object> properties) {
+    private void openScope(DebugContext debug, String name, int inlineDepth, Map<Object, Object> properties) {
         try {
             Map<Object, Object> props = properties;
             if (inlineDepth == 0) {
@@ -321,18 +295,20 @@
                 }
                 props.put("date", new Date().toString());
             }
-            printer.beginGroup(name, name, Debug.contextLookup(ResolvedJavaMethod.class), -1, props);
+            printer.beginGroup(debug, name, name, debug.contextLookup(ResolvedJavaMethod.class), -1, props);
         } catch (IOException e) {
-            handleException(e);
+            handleException(debug, e);
         }
     }
 
-    private void closeScope(int inlineDepth) {
+    private void closeScope(DebugContext debug, int inlineDepth) {
         dumpIds[inlineDepth] = 0;
         try {
-            printer.endGroup();
+            if (printer != null) {
+                printer.endGroup();
+            }
         } catch (IOException e) {
-            handleException(e);
+            handleException(debug, e);
         }
     }
 
@@ -340,7 +316,7 @@
     public void close() {
         if (previousInlineContext != null) {
             for (int inlineDepth = 0; inlineDepth < previousInlineContext.size(); inlineDepth++) {
-                closeScope(inlineDepth);
+                closeScope(null, inlineDepth);
             }
         }
         if (printer != null) {
@@ -348,14 +324,4 @@
             printer = null;
         }
     }
-
-    @Override
-    public void addCapability(Object capability) {
-        if (capability instanceof SnippetReflectionProvider) {
-            snippetReflection = (SnippetReflectionProvider) capability;
-            if (printer != null && printer.getSnippetReflectionProvider() == null) {
-                printer.setSnippetReflectionProvider(snippetReflection);
-            }
-        }
-    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java	Fri Jul 07 09:40:47 2017 -0700
@@ -32,8 +32,8 @@
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.bytecode.BytecodeDisassembler;
-import org.graalvm.compiler.debug.GraalDebugConfig.Options;
-import org.graalvm.compiler.debug.internal.DebugScope;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
@@ -50,6 +50,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
@@ -63,7 +64,7 @@
 public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPrinter {
 
     private final boolean tryToSchedule;
-    private SnippetReflectionProvider snippetReflection;
+    private final SnippetReflectionProvider snippetReflection;
 
     /**
      * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream.
@@ -71,18 +72,14 @@
      * @param tryToSchedule If false, no scheduling is done, which avoids exceptions for
      *            non-schedulable graphs.
      */
-    public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule) {
+    public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule, SnippetReflectionProvider snippetReflection) {
         super(stream);
+        this.snippetReflection = snippetReflection;
         this.begin();
         this.tryToSchedule = tryToSchedule;
     }
 
     @Override
-    public void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection) {
-        this.snippetReflection = snippetReflection;
-    }
-
-    @Override
     public SnippetReflectionProvider getSnippetReflectionProvider() {
         return snippetReflection;
     }
@@ -92,7 +89,7 @@
      * as properties.
      */
     @Override
-    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) {
+    public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) {
         beginGroup();
         beginProperties();
         printProperty("name", name);
@@ -114,8 +111,8 @@
      * nodes.
      */
     @Override
-    public void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) {
-        String title = formatTitle(id, format, args);
+    public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) {
+        String title = id + ": " + String.format(format, simplifyClassArgs(args));
         beginGraph(title);
         EconomicSet<Node> noBlockNodes = EconomicSet.create(Equivalence.IDENTITY);
         ScheduleResult schedule = null;
@@ -123,9 +120,10 @@
             StructuredGraph structuredGraph = (StructuredGraph) graph;
             schedule = structuredGraph.getLastSchedule();
             if (schedule == null && tryToSchedule) {
-                if (Options.PrintGraphWithSchedule.getValue(DebugScope.getConfig().getOptions())) {
+                OptionValues options = graph.getOptions();
+                if (DebugOptions.PrintGraphWithSchedule.getValue(options)) {
                     try {
-                        SchedulePhase schedulePhase = new SchedulePhase(graph.getOptions());
+                        SchedulePhase schedulePhase = new SchedulePhase(options);
                         schedulePhase.apply(structuredGraph);
                         schedule = structuredGraph.getLastSchedule();
                     } catch (Throwable t) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,9 +26,9 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugVerifyHandler;
 import org.graalvm.compiler.debug.GraalError;
-import org.graalvm.compiler.debug.internal.DebugScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.options.Option;
@@ -63,8 +63,8 @@
     private static final Map<String, Boolean> discovered = new ConcurrentHashMap<>();
 
     @Override
-    public void verify(Object object, String format, Object... args) {
-        OptionValues options = DebugScope.getConfig().getOptions();
+    public void verify(DebugContext debug, Object object, String format, Object... args) {
+        OptionValues options = debug.getOptions();
         if (Options.NDCV.getValue(options) != OFF && object instanceof StructuredGraph) {
             StructuredGraph graph = (StructuredGraph) object;
             List<Node> before = graph.getNodes().snapshot();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -54,8 +55,9 @@
     private final SnippetTemplate.SnippetInfo drem;
     private final SnippetTemplate.SnippetInfo frem;
 
-    public AArch64FloatArithmeticSnippets(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-        super(options, providers, snippetReflection, target);
+    public AArch64FloatArithmeticSnippets(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection,
+                    TargetDescription target) {
+        super(options, factories, providers, snippetReflection, target);
         drem = snippet(AArch64FloatArithmeticSnippets.class, "dremSnippet");
         frem = snippet(AArch64FloatArithmeticSnippets.class, "fremSnippet");
     }
@@ -72,7 +74,7 @@
         Arguments args = new Arguments(snippet, graph.getGuardsStage(), tool.getLoweringStage());
         args.add("x", node.getX());
         args.add("y", node.getY());
-        template(args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, tool, args);
+        template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, tool, args);
     }
 
     @Snippet
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,6 +25,7 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.graph.NodeClass;
@@ -68,8 +69,9 @@
     private final SnippetTemplate.SnippetInfo uirem;
     private final SnippetTemplate.SnippetInfo ulrem;
 
-    public AArch64IntegerArithmeticSnippets(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-        super(options, providers, snippetReflection, target);
+    public AArch64IntegerArithmeticSnippets(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection,
+                    TargetDescription target) {
+        super(options, factories, providers, snippetReflection, target);
         idiv = snippet(AArch64IntegerArithmeticSnippets.class, "idivSnippet");
         ldiv = snippet(AArch64IntegerArithmeticSnippets.class, "ldivSnippet");
         irem = snippet(AArch64IntegerArithmeticSnippets.class, "iremSnippet");
@@ -103,7 +105,7 @@
         Arguments args = new Arguments(snippet, graph.getGuardsStage(), tool.getLoweringStage());
         args.add("x", node.getX());
         args.add("y", node.getY());
-        template(args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args);
+        template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args);
     }
 
     @Snippet
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64ConvertSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64ConvertSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,7 +28,7 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.calc.FloatConvertNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -155,8 +155,8 @@
         private final SnippetInfo d2i;
         private final SnippetInfo d2l;
 
-        public Templates(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-            super(options, providers, snippetReflection, target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
+            super(options, factories, providers, snippetReflection, target);
 
             f2i = snippet(AMD64ConvertSnippets.class, "f2i");
             f2l = snippet(AMD64ConvertSnippets.class, "f2l");
@@ -189,8 +189,8 @@
             args.add("input", convert.getValue());
             args.add("result", graph.unique(new AMD64FloatConvertNode(convert.getFloatConvert(), convert.getValue())));
 
-            SnippetTemplate template = template(args);
-            Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.getFloatConvert(), graph, convert, template, args);
+            SnippetTemplate template = template(convert.getDebug(), args);
+            convert.getDebug().log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.getFloatConvert(), graph, convert, template, args);
             template.instantiate(providers.getMetaAccess(), convert, DEFAULT_REPLACER, tool, args);
             convert.safeDelete();
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledExceptionHandlerTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledExceptionHandlerTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,16 +22,17 @@
  */
 package org.graalvm.compiler.replacements.test;
 
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.phases.HighTier;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo;
 import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.junit.Assert;
 import org.junit.Test;
@@ -59,8 +60,8 @@
     }
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        StructuredGraph graph = super.parseEager(m, allowAssumptions, compilationId, options);
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        StructuredGraph graph = super.parse(builder, graphBuilderSuite);
         int handlers = graph.getNodes().filter(ExceptionObjectNode.class).count();
         Assert.assertEquals(1, handlers);
         return graph;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledNullPointerExceptionTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/CompiledNullPointerExceptionTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,17 +24,18 @@
 
 import static org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.BytecodeExceptionMode.CheckAll;
 
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.core.phases.HighTier;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.junit.Assert;
 import org.junit.Test;
@@ -63,8 +64,8 @@
     }
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        StructuredGraph graph = super.parseEager(m, allowAssumptions, compilationId, options);
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        StructuredGraph graph = super.parse(builder, graphBuilderSuite);
         int handlers = graph.getNodes().filter(BytecodeExceptionNode.class).count();
         Assert.assertEquals(1, handlers);
         return graph;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DerivedOopTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DerivedOopTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,8 +25,8 @@
 import java.util.Objects;
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Scope;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -147,7 +147,8 @@
     public void testFieldOffsetMergeNonLiveBasePointer() {
         thrown.expect(GraalError.class);
         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
-        try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) {
+        DebugContext debug = getDebugContext();
+        try (Scope s = debug.disable()) {
             // Run a couple times to encourage objects to move
             for (int i = 0; i < 4; i++) {
                 Result r = new Result();
@@ -171,7 +172,8 @@
     public void testFieldOffsetMergeLiveBasePointer() {
         thrown.expect(GraalError.class);
         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
-        try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) {
+        DebugContext debug = getDebugContext();
+        try (Scope s = debug.disable()) {
             // Run a couple times to encourage objects to move
             for (int i = 0; i < 4; i++) {
                 Result r = new Result();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EdgesTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EdgesTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -42,6 +42,7 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
+import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.common.inlining.policy.InlineMethodSubstitutionsPolicy;
@@ -64,7 +65,12 @@
 
     }
 
-    StructuredGraph graph = new StructuredGraph.Builder(getInitialOptions()).build();
+    protected StructuredGraph createGraph() {
+        OptionValues options = getInitialOptions();
+        return new StructuredGraph.Builder(options, getDebugContext(options)).build();
+    }
+
+    StructuredGraph graph = createGraph();
     TestNode node;
     ConstantNode i1;
     ConstantNode i2;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/InstanceOfTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/InstanceOfTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,16 +28,14 @@
 import java.util.Map;
 import java.util.TreeMap;
 
-import org.junit.Test;
-
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
 import org.graalvm.compiler.phases.common.AbstractInliningPhase;
+import org.junit.Test;
 
 import jdk.vm.ci.code.site.Call;
 import jdk.vm.ci.code.site.Mark;
@@ -485,13 +483,14 @@
 
     @SuppressWarnings("try")
     protected StructuredGraph buildGraph(final String snippet) {
-        try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             compile(graph.method(), graph);
-            Debug.dump(Debug.BASIC_LEVEL, graph, snippet);
+            debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(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	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,8 +26,7 @@
 
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -53,12 +52,13 @@
 
     @SuppressWarnings("try")
     protected StructuredGraph testGraph(final String snippet) {
-        try (Scope s = Debug.scope("MethodSubstitutionTest", getResolvedJavaMethod(snippet))) {
-            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("MethodSubstitutionTest", getResolvedJavaMethod(snippet))) {
+            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             HighTierContext context = getDefaultHighTierContext();
-            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+            debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
             new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
-            Debug.dump(Debug.BASIC_LEVEL, graph, "Graph");
+            debug.dump(DebugContext.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
@@ -72,7 +72,7 @@
             assertNotInGraph(graph, Invoke.class);
             return graph;
         } catch (Throwable e) {
-            throw Debug.handle(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/PEGraphDecoderTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,10 +24,9 @@
 
 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createStandardInlineInfo;
 
-import org.junit.Test;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -47,6 +46,7 @@
 import org.graalvm.compiler.phases.tiers.PhaseContext;
 import org.graalvm.compiler.replacements.CachingPEGraphDecoder;
 import org.graalvm.word.LocationIdentity;
+import org.junit.Test;
 
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -130,15 +130,16 @@
     public void test() {
         ResolvedJavaMethod testMethod = getResolvedJavaMethod(PEGraphDecoderTest.class, "doTest", Object.class);
         StructuredGraph targetGraph = null;
-        try (Debug.Scope scope = Debug.scope("GraphPETest", testMethod)) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope scope = debug.scope("GraphPETest", testMethod)) {
             GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withEagerResolving(true);
             registerPlugins(graphBuilderConfig.getPlugins().getInvocationPlugins());
-            targetGraph = new StructuredGraph.Builder(getInitialOptions(), AllowAssumptions.YES).method(testMethod).build();
+            targetGraph = new StructuredGraph.Builder(getInitialOptions(), debug, 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, null);
+                            null, null, new InlineInvokePlugin[]{new InlineAll()}, null, null);
 
             decoder.decode(testMethod);
-            Debug.dump(Debug.BASIC_LEVEL, targetGraph, "Target Graph");
+            debug.dump(DebugContext.BASIC_LEVEL, targetGraph, "Target Graph");
             targetGraph.verify();
 
             PhaseContext context = new PhaseContext(getProviders());
@@ -147,9 +148,9 @@
 
         } catch (Throwable ex) {
             if (targetGraph != null) {
-                Debug.dump(Debug.BASIC_LEVEL, targetGraph, ex.toString());
+                debug.dump(DebugContext.BASIC_LEVEL, targetGraph, ex.toString());
             }
-            Debug.handle(ex);
+            debug.handle(ex);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTrackingTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTrackingTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,8 +24,6 @@
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.bytecode.BytecodeProvider;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
@@ -82,9 +80,7 @@
     @Test(expected = GraalError.class)
     @SuppressWarnings("try")
     public void testVerification() {
-        try (DebugConfigScope scope = Debug.disableIntercept()) {
-            compile(getResolvedJavaMethod("verificationSnippet"), null);
-        }
+        compile(getResolvedJavaMethod("verificationSnippet"), null);
     }
 
     public static long verificationSnippet(Object obj) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,8 +30,6 @@
 import org.graalvm.compiler.api.replacements.ClassSubstitution;
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
 import org.graalvm.compiler.bytecode.BytecodeProvider;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugConfigScope;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
@@ -471,9 +469,7 @@
         byte[] in = {0, 1, 2, 3, 4};
         byte[] out = new byte[in.length];
         try {
-            try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) {
-                test("callCopyFirstL2R", in, out);
-            }
+            test("callCopyFirstL2R", in, out);
         } catch (GraalGraphError e) {
             assertTrue(e.getMessage().startsWith("Invalid frame state"));
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SnippetsTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SnippetsTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,15 +22,13 @@
  */
 package org.graalvm.compiler.replacements.test;
 
-import org.graalvm.compiler.core.common.CompilationIdentifier;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.replacements.ReplacementsImpl;
 import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
 
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
 public abstract class SnippetsTest extends ReplacementsTest {
 
     protected final ReplacementsImpl installer;
@@ -39,12 +37,12 @@
     protected SnippetsTest() {
         ReplacementsImpl d = (ReplacementsImpl) getReplacements();
         bytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        installer = new ReplacementsImpl(getInitialOptions(), d.providers, d.snippetReflection, bytecodeProvider, d.target);
+        installer = new ReplacementsImpl(getInitialOptions(), null, d.providers, d.snippetReflection, bytecodeProvider, d.target);
         installer.setGraphBuilderPlugins(d.getGraphBuilderPlugins());
     }
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
-        return installer.makeGraph(bytecodeProvider, m, null, null);
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        return installer.makeGraph(getDebugContext(), bytecodeProvider, builder.getMethod(), null, null);
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/WordTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/WordTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,10 +23,11 @@
 package org.graalvm.compiler.replacements.test;
 
 import org.graalvm.compiler.api.replacements.Snippet;
-import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.word.Word;
 import org.graalvm.word.Pointer;
 import org.graalvm.word.Unsigned;
@@ -34,17 +35,17 @@
 import org.graalvm.word.WordFactory;
 import org.junit.Test;
 
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
 /**
  * Tests for the {@link Word} type.
  */
 public class WordTest extends SnippetsTest {
 
     @Override
-    protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) {
+    protected StructuredGraph parse(Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
         // create a copy to assign a valid compilation id
-        return installer.makeGraph(bytecodeProvider, m, null, null).copyWithIdentifier(compilationId);
+        DebugContext debug = getDebugContext();
+        StructuredGraph originalGraph = installer.makeGraph(debug, bytecodeProvider, builder.getMethod(), null, null);
+        return originalGraph.copyWithIdentifier(builder.getCompilationId(), debug);
     }
 
     @Test
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,8 +29,8 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -185,8 +185,9 @@
         private final SnippetCounter valueOfCounter;
         private final SnippetCounter valueCounter;
 
-        public Templates(OptionValues options, SnippetCounter.Group.Factory factory, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-            super(options, providers, snippetReflection, target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, Providers providers, SnippetReflectionProvider snippetReflection,
+                        TargetDescription target) {
+            super(options, factories, providers, snippetReflection, target);
             for (JavaKind kind : new JavaKind[]{JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Double, JavaKind.Float, JavaKind.Int, JavaKind.Long, JavaKind.Short}) {
                 boxSnippets.put(kind, snippet(BoxingSnippets.class, kind.getJavaName() + "ValueOf"));
                 unboxSnippets.put(kind, snippet(BoxingSnippets.class, kind.getJavaName() + "Value"));
@@ -206,8 +207,8 @@
                 args.add("value", box.getValue());
                 args.addConst("valueOfCounter", valueOfCounter);
 
-                SnippetTemplate template = template(args);
-                Debug.log("Lowering integerValueOf in %s: node=%s, template=%s, arguments=%s", box.graph(), box, template, args);
+                SnippetTemplate template = template(box.getDebug(), args);
+                box.getDebug().log("Lowering integerValueOf in %s: node=%s, template=%s, arguments=%s", box.graph(), box, template, args);
                 template.instantiate(providers.getMetaAccess(), box, DEFAULT_REPLACER, args);
             }
         }
@@ -217,8 +218,8 @@
             args.add("value", unbox.getValue());
             args.addConst("valueCounter", valueCounter);
 
-            SnippetTemplate template = template(args);
-            Debug.log("Lowering integerValueOf in %s: node=%s, template=%s, arguments=%s", unbox.graph(), unbox, template, args);
+            SnippetTemplate template = template(unbox.getDebug(), args);
+            unbox.getDebug().log("Lowering integerValueOf in %s: node=%s, template=%s, arguments=%s", unbox.graph(), unbox, template, args);
             template.instantiate(providers.getMetaAccess(), unbox, DEFAULT_REPLACER, args);
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,7 +25,7 @@
 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
 
 import org.graalvm.compiler.bytecode.BytecodeProvider;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.java.GraphBuilderPhase;
 import org.graalvm.compiler.nodes.EncodedGraph;
 import org.graalvm.compiler.nodes.GraphEncoder;
@@ -38,7 +38,6 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin;
 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;
 import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase;
@@ -62,9 +61,10 @@
     private final EconomicMap<ResolvedJavaMethod, EncodedGraph> graphCache;
 
     public CachingPEGraphDecoder(Architecture architecture, StructuredGraph graph, Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
-                    AllowAssumptions allowAssumptions, OptionValues options, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
-                    ParameterPlugin parameterPlugin, NodePlugin[] nodePlugins) {
-        super(architecture, graph, providers.getMetaAccess(), providers.getConstantReflection(), providers.getConstantFieldProvider(), providers.getStampProvider(), options, loopExplosionPlugin,
+                    AllowAssumptions allowAssumptions, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
+                    ParameterPlugin parameterPlugin,
+                    NodePlugin[] nodePlugins) {
+        super(architecture, graph, providers.getMetaAccess(), providers.getConstantReflection(), providers.getConstantFieldProvider(), providers.getStampProvider(), loopExplosionPlugin,
                         invocationPlugins, inlineInvokePlugins, parameterPlugin, nodePlugins);
 
         this.providers = providers;
@@ -81,8 +81,8 @@
 
     @SuppressWarnings("try")
     private EncodedGraph createGraph(ResolvedJavaMethod method, BytecodeProvider intrinsicBytecodeProvider) {
-        StructuredGraph graphToEncode = new StructuredGraph.Builder(options, allowAssumptions).useProfilingInfo(false).method(method).build();
-        try (Debug.Scope scope = Debug.scope("createGraph", graphToEncode)) {
+        StructuredGraph graphToEncode = new StructuredGraph.Builder(options, debug, allowAssumptions).useProfilingInfo(false).method(method).build();
+        try (DebugContext.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(graphToEncode);
@@ -101,7 +101,7 @@
             return encodedGraph;
 
         } catch (Throwable ex) {
-            throw Debug.handle(ex);
+            throw debug.handle(ex);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantStringIndexOfSnippets.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantStringIndexOfSnippets.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,6 +26,7 @@
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -44,8 +45,8 @@
 
         private final SnippetInfo indexOfConstant = snippet(ConstantStringIndexOfSnippets.class, "indexOfConstant");
 
-        public Templates(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-            super(options, providers, snippetReflection, target);
+        public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
+            super(options, factories, providers, snippetReflection, target);
         }
 
         public void lower(SnippetLowerableMemoryNode stringIndexOf, LoweringTool tool) {
@@ -61,7 +62,7 @@
             char[] targetCharArray = snippetReflection.asObject(char[].class, stringIndexOf.getArgument(3).asJavaConstant());
             args.addConst("md2", md2(targetCharArray));
             args.addConst("cache", computeCache(targetCharArray));
-            template(args).instantiate(providers.getMetaAccess(), stringIndexOf, DEFAULT_REPLACER, args);
+            template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), stringIndexOf, DEFAULT_REPLACER, args);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Fri Jul 07 09:40:47 2017 -0700
@@ -46,6 +46,7 @@
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodeinfo.InputType;
@@ -157,10 +158,10 @@
         this.target = target;
     }
 
-    public void initialize(OptionValues options, SnippetCounter.Group.Factory factory, Providers providers, SnippetReflectionProvider snippetReflection) {
-        boxingSnippets = new BoxingSnippets.Templates(options, factory, providers, snippetReflection, target);
-        indexOfSnippets = new ConstantStringIndexOfSnippets.Templates(options, providers, snippetReflection, target);
-        providers.getReplacements().registerSnippetTemplateCache(new SnippetCounterNode.SnippetCounterSnippets.Templates(options, providers, snippetReflection, target));
+    public void initialize(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, Providers providers, SnippetReflectionProvider snippetReflection) {
+        boxingSnippets = new BoxingSnippets.Templates(options, factories, factory, providers, snippetReflection, target);
+        indexOfSnippets = new ConstantStringIndexOfSnippets.Templates(options, factories, providers, snippetReflection, target);
+        providers.getReplacements().registerSnippetTemplateCache(new SnippetCounterNode.SnippetCounterSnippets.Templates(options, factories, providers, snippetReflection, target));
     }
 
     public final TargetDescription getTarget() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java	Fri Jul 07 09:40:47 2017 -0700
@@ -307,7 +307,7 @@
         Plugins plugins = new Plugins(graphBuilderPlugins);
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
 
-        StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions()).method(method).build();
+        StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions(), invoke.getDebug()).method(method).build();
         IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING);
         GraphBuilderPhase.Instance instance = new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), providers.getConstantFieldProvider(), config,
                         OptimisticOptimizations.NONE,
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.core.common.calc.Condition;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ConditionAnchorNode;
 import org.graalvm.compiler.nodes.ConstantNode;
@@ -70,8 +71,8 @@
  */
 public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates {
 
-    public InstanceOfSnippetsTemplates(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-        super(options, providers, snippetReflection, target);
+    public InstanceOfSnippetsTemplates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
+        super(options, factories, providers, snippetReflection, target);
     }
 
     /**
@@ -94,7 +95,7 @@
                 replacer.replaceUsingInstantiation();
             } else {
                 Arguments args = makeArguments(replacer, tool);
-                template(args).instantiate(providers.getMetaAccess(), instanceOf, replacer, tool, args);
+                template(instanceOf.getDebug(), args).instantiate(providers.getMetaAccess(), instanceOf, replacer, tool, args);
             }
         }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,6 +29,7 @@
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
 import org.graalvm.compiler.core.common.type.TypeReference;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
@@ -76,22 +77,20 @@
     protected ValueNode[] arguments;
     protected ValueNode returnValue;
 
-    public IntrinsicGraphBuilder(OptionValues options, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
-                    StampProvider stampProvider,
-                    Bytecode code, int invokeBci) {
-        this(options, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci, AllowAssumptions.YES);
+    public IntrinsicGraphBuilder(OptionValues options, DebugContext debug, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
+                    StampProvider stampProvider, Bytecode code, int invokeBci) {
+        this(options, debug, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci, AllowAssumptions.YES);
     }
 
-    protected IntrinsicGraphBuilder(OptionValues options, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
-                    StampProvider stampProvider,
-                    Bytecode code, int invokeBci, AllowAssumptions allowAssumptions) {
+    protected IntrinsicGraphBuilder(OptionValues options, DebugContext debug, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
+                    StampProvider stampProvider, Bytecode code, int invokeBci, AllowAssumptions allowAssumptions) {
         this.metaAccess = metaAccess;
         this.constantReflection = constantReflection;
         this.constantFieldProvider = constantFieldProvider;
         this.stampProvider = stampProvider;
         this.code = code;
         this.method = code.getMethod();
-        this.graph = new StructuredGraph.Builder(options, allowAssumptions).method(method).build();
+        this.graph = new StructuredGraph.Builder(options, debug, allowAssumptions).method(method).build();
         this.invokeBci = invokeBci;
         this.lastInstr = graph.start();
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java	Fri Jul 07 09:40:47 2017 -0700
@@ -40,8 +40,8 @@
 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;
 import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -413,7 +413,6 @@
         }
     }
 
-    protected final OptionValues options;
     private final LoopExplosionPlugin loopExplosionPlugin;
     private final InvocationPlugins invocationPlugins;
     private final InlineInvokePlugin[] inlineInvokePlugins;
@@ -423,14 +422,14 @@
     private final EconomicMap<ResolvedJavaMethod, Object> invocationPluginCache;
 
     public PEGraphDecoder(Architecture architecture, StructuredGraph graph, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
-                    StampProvider stampProvider, OptionValues options, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
-                    ParameterPlugin parameterPlugin, NodePlugin[] nodePlugins) {
+                    StampProvider stampProvider, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
+                    ParameterPlugin parameterPlugin,
+                    NodePlugin[] nodePlugins) {
         super(architecture, graph, metaAccess, constantReflection, constantFieldProvider, stampProvider, true);
         this.loopExplosionPlugin = loopExplosionPlugin;
         this.invocationPlugins = invocationPlugins;
         this.inlineInvokePlugins = inlineInvokePlugins;
         this.parameterPlugin = parameterPlugin;
-        this.options = options;
         this.nodePlugins = nodePlugins;
         this.specialCallTargetCache = EconomicMap.create(Equivalence.DEFAULT);
         this.invocationPluginCache = EconomicMap.create(Equivalence.DEFAULT);
@@ -449,7 +448,7 @@
         decode(createInitialLoopScope(methodScope, null));
         cleanupGraph(methodScope);
 
-        Debug.dump(Debug.VERBOSE_LEVEL, graph, "After graph cleanup");
+        debug.dump(DebugContext.VERBOSE_LEVEL, graph, "After graph cleanup");
         assert graph.verify();
 
         try {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,14 +23,18 @@
 package org.graalvm.compiler.replacements;
 
 import static org.graalvm.compiler.core.common.GraalOptions.UseSnippetGraphCache;
+import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineIntrinsicsDuringParsing;
 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.graalvm.compiler.api.replacements.Fold;
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
@@ -42,11 +46,12 @@
 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
 import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
 import org.graalvm.compiler.debug.DebugCloseable;
-import org.graalvm.compiler.debug.DebugTimer;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Description;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.java.GraphBuilderPhase;
@@ -92,6 +97,7 @@
     public final SnippetReflectionProvider snippetReflection;
     public final TargetDescription target;
     private GraphBuilderConfiguration.Plugins graphBuilderPlugins;
+    private final DebugHandlersFactory debugHandlersFactory;
 
     @Override
     public OptionValues getOptions() {
@@ -180,7 +186,8 @@
     // it is stable across VM executions (in support of replay compilation).
     private final EconomicMap<String, SnippetTemplateCache> snippetTemplateCache;
 
-    public ReplacementsImpl(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target) {
+    public ReplacementsImpl(OptionValues options, DebugHandlersFactory debugHandlersFactory, Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider,
+                    TargetDescription target) {
         this.options = options;
         this.providers = providers.copyWith(this);
         this.snippetReflection = snippetReflection;
@@ -188,15 +195,26 @@
         this.graphs = new ConcurrentHashMap<>();
         this.snippetTemplateCache = EconomicMap.create(Equivalence.DEFAULT);
         this.defaultBytecodeProvider = bytecodeProvider;
+        this.debugHandlersFactory = debugHandlersFactory;
+
     }
 
-    private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime");
+    private static final TimerKey SnippetPreparationTime = DebugContext.timer("SnippetPreparationTime");
 
     @Override
     public StructuredGraph getSnippet(ResolvedJavaMethod method, Object[] args) {
         return getSnippet(method, null, args);
     }
 
+    private static final AtomicInteger nextDebugContextId = new AtomicInteger();
+
+    protected DebugContext openDebugContext(String idPrefix, ResolvedJavaMethod method) {
+        DebugContext outer = DebugContext.forCurrentThread();
+        Description description = new Description(method, idPrefix + nextDebugContextId.incrementAndGet());
+        List<DebugHandlersFactory> factories = debugHandlersFactory == null ? Collections.emptyList() : Collections.singletonList(debugHandlersFactory);
+        return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, factories);
+    }
+
     @Override
     @SuppressWarnings("try")
     public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args) {
@@ -205,12 +223,14 @@
 
         StructuredGraph graph = UseSnippetGraphCache.getValue(options) ? graphs.get(method) : null;
         if (graph == null) {
-            try (DebugCloseable a = SnippetPreparationTime.start()) {
-                StructuredGraph newGraph = makeGraph(defaultBytecodeProvider, method, args, recursiveEntry);
-                Debug.counter("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
+            try (DebugContext debug = openDebugContext("Snippet_", method);
+                            DebugCloseable a = SnippetPreparationTime.start(debug)) {
+                StructuredGraph newGraph = makeGraph(debug, defaultBytecodeProvider, method, args, recursiveEntry);
+                DebugContext.counter("SnippetNodeCount[%#s]", method).add(newGraph.getDebug(), newGraph.getNodeCount());
                 if (!UseSnippetGraphCache.getValue(options) || args != null) {
                     return newGraph;
                 }
+                newGraph.freeze();
                 graphs.putIfAbsent(method, newGraph);
                 graph = graphs.get(method);
             }
@@ -256,10 +276,12 @@
                 ResolvedJavaMethod substitute = msPlugin.getSubstitute(metaAccess);
                 StructuredGraph graph = graphs.get(substitute);
                 if (graph == null) {
-                    graph = makeGraph(msPlugin.getBytecodeProvider(), substitute, null, method);
-                    graph.freeze();
-                    graphs.putIfAbsent(substitute, graph);
-                    graph = graphs.get(substitute);
+                    try (DebugContext debug = openDebugContext("Substitution_", method)) {
+                        graph = makeGraph(debug, msPlugin.getBytecodeProvider(), substitute, null, method);
+                        graph.freeze();
+                        graphs.putIfAbsent(substitute, graph);
+                        graph = graphs.get(substitute);
+                    }
                 }
                 assert graph.isFrozen();
                 result = graph;
@@ -268,7 +290,9 @@
                 ConstantReflectionProvider constantReflection = providers.getConstantReflection();
                 ConstantFieldProvider constantFieldProvider = providers.getConstantFieldProvider();
                 StampProvider stampProvider = providers.getStampProvider();
-                result = new IntrinsicGraphBuilder(options, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci).buildGraph(plugin);
+                try (DebugContext debug = openDebugContext("Substitution_", method)) {
+                    result = new IntrinsicGraphBuilder(options, debug, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci).buildGraph(plugin);
+                }
             }
         } else {
             result = null;
@@ -285,8 +309,8 @@
      * @param original the original method if {@code method} is a {@linkplain MethodSubstitution
      *            substitution} otherwise null
      */
-    public StructuredGraph makeGraph(BytecodeProvider bytecodeProvider, ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
-        return createGraphMaker(method, original).makeGraph(bytecodeProvider, args);
+    public StructuredGraph makeGraph(DebugContext debug, BytecodeProvider bytecodeProvider, ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
+        return createGraphMaker(method, original).makeGraph(debug, bytecodeProvider, args);
     }
 
     /**
@@ -323,18 +347,18 @@
         }
 
         @SuppressWarnings("try")
-        public StructuredGraph makeGraph(BytecodeProvider bytecodeProvider, Object[] args) {
-            try (Scope s = Debug.scope("BuildSnippetGraph", method)) {
+        public StructuredGraph makeGraph(DebugContext debug, BytecodeProvider bytecodeProvider, Object[] args) {
+            try (DebugContext.Scope s = debug.scope("BuildSnippetGraph", method)) {
                 assert method.hasBytecodes() : method;
-                StructuredGraph graph = buildInitialGraph(bytecodeProvider, method, args);
+                StructuredGraph graph = buildInitialGraph(debug, bytecodeProvider, method, args);
 
                 finalizeGraph(graph);
 
-                Debug.dump(Debug.INFO_LEVEL, graph, "%s: Final", method.getName());
+                debug.dump(DebugContext.INFO_LEVEL, graph, "%s: Final", method.getName());
 
                 return graph;
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
         }
 
@@ -390,16 +414,16 @@
          * Builds the initial graph for a replacement.
          */
         @SuppressWarnings("try")
-        protected StructuredGraph buildInitialGraph(BytecodeProvider bytecodeProvider, final ResolvedJavaMethod methodToParse, Object[] args) {
+        protected StructuredGraph buildInitialGraph(DebugContext debug, BytecodeProvider bytecodeProvider, final ResolvedJavaMethod methodToParse, Object[] args) {
             // Replacements cannot have optimistic assumptions since they have
             // to be valid for the entire run of the VM.
+            final StructuredGraph graph = new StructuredGraph.Builder(replacements.options, debug).method(methodToParse).build();
 
-            final StructuredGraph graph = new StructuredGraph.Builder(replacements.options).method(methodToParse).build();
-
-            // They are not user code so they do not participate in unsafe access tracking
+            // Replacements are not user code so they do not participate in unsafe access
+            // tracking
             graph.disableUnsafeAccessTracking();
 
-            try (Scope s = Debug.scope("buildInitialGraph", graph)) {
+            try (DebugContext.Scope s = debug.scope("buildInitialGraph", graph)) {
                 MetaAccessProvider metaAccess = replacements.providers.getMetaAccess();
 
                 Plugins plugins = new Plugins(replacements.graphBuilderPlugins);
@@ -423,7 +447,7 @@
 
                 new CanonicalizerPhase().apply(graph, new PhaseContext(replacements.providers));
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
             return graph;
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounterNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounterNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -34,6 +34,7 @@
 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -145,8 +146,8 @@
 
             private final SnippetInfo add = snippet(SnippetCounterSnippets.class, "add", SNIPPET_COUNTER_LOCATION);
 
-            Templates(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
-                super(options, providers, snippetReflection, target);
+            Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
+                super(options, factories, providers, snippetReflection, target);
             }
 
             public void lower(SnippetCounterNode counter, LoweringTool tool) {
@@ -155,7 +156,7 @@
                 args.addConst("counter", counter.getCounter());
                 args.add("increment", counter.getIncrement());
 
-                template(args).instantiate(providers.getMetaAccess(), counter, DEFAULT_REPLACER, args);
+                template(counter.getDebug(), args).instantiate(providers.getMetaAccess(), counter, DEFAULT_REPLACER, args);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Fri Jul 07 09:40:47 2017 -0700
@@ -23,8 +23,9 @@
 package org.graalvm.compiler.replacements;
 
 import static java.util.FormattableFlags.ALTERNATE;
-import static org.graalvm.compiler.debug.Debug.applyFormattingFlagsAndWidth;
-import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugStubsAndSnippets;
+import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
+import static org.graalvm.compiler.debug.DebugContext.applyFormattingFlagsAndWidth;
+import static org.graalvm.compiler.debug.DebugOptions.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;
@@ -42,6 +43,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
 
@@ -55,14 +57,13 @@
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.StampPair;
 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.CounterKey;
 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.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugContext.Description;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.TimerKey;
 import org.graalvm.compiler.graph.Graph.Mark;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -227,22 +228,22 @@
          *
          * @see SnippetTemplate#instantiationTimer
          */
-        private final DebugTimer instantiationTimer;
+        private final TimerKey instantiationTimer;
 
         /**
          * Counts instantiations of all templates derived from this snippet.
          *
          * @see SnippetTemplate#instantiationCounter
          */
-        private final DebugCounter instantiationCounter;
+        private final CounterKey instantiationCounter;
 
         protected abstract Lazy lazy();
 
         protected SnippetInfo(ResolvedJavaMethod method, LocationIdentity[] privateLocations) {
             this.method = method;
             this.privateLocations = privateLocations;
-            instantiationCounter = Debug.counter("SnippetInstantiationCount[%s]", method.getName());
-            instantiationTimer = Debug.timer("SnippetInstantiationTime[%s]", method.getName());
+            instantiationCounter = DebugContext.counter("SnippetInstantiationCount[%s]", method.getName());
+            instantiationTimer = DebugContext.timer("SnippetInstantiationTime[%s]", method.getName());
             assert method.isStatic() : "snippet method must be static: " + method.format("%H.%n");
         }
 
@@ -562,8 +563,8 @@
         }
     }
 
-    private static final DebugTimer SnippetTemplateCreationTime = Debug.timer("SnippetTemplateCreationTime");
-    private static final DebugCounter SnippetTemplates = Debug.counter("SnippetTemplateCount");
+    private static final TimerKey SnippetTemplateCreationTime = DebugContext.timer("SnippetTemplateCreationTime");
+    private static final CounterKey SnippetTemplates = DebugContext.counter("SnippetTemplateCount");
 
     static class Options {
         @Option(help = "Use a LRU cache for snippet templates.")//
@@ -581,14 +582,16 @@
         protected final OptionValues options;
         protected final Providers providers;
         protected final SnippetReflectionProvider snippetReflection;
+        protected final Iterable<DebugHandlersFactory> factories;
         protected final TargetDescription target;
         private final Map<CacheKey, SnippetTemplate> templates;
 
-        protected AbstractTemplates(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
+        protected AbstractTemplates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
             this.options = options;
             this.providers = providers;
             this.snippetReflection = snippetReflection;
             this.target = target;
+            this.factories = factories;
             if (Options.UseSnippetTemplateCache.getValue(options)) {
                 int size = Options.MaxTemplatesPerSnippet.getValue(options);
                 this.templates = Collections.synchronizedMap(new LRUCache<>(size, size));
@@ -627,22 +630,33 @@
             }
         }
 
+        static final AtomicInteger nextSnippetTemplateId = new AtomicInteger();
+
+        private DebugContext openDebugContext(DebugContext outer, Arguments args) {
+            if (DebugStubsAndSnippets.getValue(options)) {
+                Description description = new Description(args.cacheKey.method, "SnippetTemplate_" + nextSnippetTemplateId.incrementAndGet());
+                return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, factories);
+            }
+            return DebugContext.DISABLED;
+        }
+
         /**
          * Gets a template for a given key, creating it first if necessary.
          */
         @SuppressWarnings("try")
-        protected SnippetTemplate template(final Arguments args) {
+        protected SnippetTemplate template(DebugContext outer, final Arguments args) {
             SnippetTemplate template = Options.UseSnippetTemplateCache.getValue(options) && args.cacheable ? templates.get(args.cacheKey) : null;
             if (template == null) {
-                SnippetTemplates.increment();
-                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);
+                try (DebugContext debug = openDebugContext(outer, args)) {
+                    try (DebugCloseable a = SnippetTemplateCreationTime.start(debug); DebugContext.Scope s = debug.scope("SnippetSpecialization", args.info.method)) {
+                        SnippetTemplates.increment(debug);
+                        template = new SnippetTemplate(options, debug, providers, snippetReflection, args);
+                        if (Options.UseSnippetTemplateCache.getValue(options) && args.cacheable) {
+                            templates.put(args.cacheKey, template);
+                        }
+                    } catch (Throwable e) {
+                        throw debug.handle(e);
                     }
-                } catch (Throwable e) {
-                    throw Debug.handle(e);
                 }
             }
             return template;
@@ -686,14 +700,14 @@
      * Creates a snippet template.
      */
     @SuppressWarnings("try")
-    protected SnippetTemplate(OptionValues options, final Providers providers, SnippetReflectionProvider snippetReflection, Arguments args) {
+    protected SnippetTemplate(OptionValues options, DebugContext debug, final Providers providers, SnippetReflectionProvider snippetReflection, Arguments args) {
         this.snippetReflection = snippetReflection;
         this.info = args.info;
 
         Object[] constantArgs = getConstantArgs(args);
         StructuredGraph snippetGraph = providers.getReplacements().getSnippet(args.info.method, args.info.original, constantArgs);
-        instantiationTimer = Debug.timer("SnippetTemplateInstantiationTime[%#s]", args);
-        instantiationCounter = Debug.counter("SnippetTemplateInstantiationCount[%#s]", args);
+        instantiationTimer = DebugContext.timer("SnippetTemplateInstantiationTime[%#s]", args);
+        instantiationCounter = DebugContext.counter("SnippetTemplateInstantiationCount[%#s]", args);
 
         ResolvedJavaMethod method = snippetGraph.method();
         Signature signature = method.getSignature();
@@ -701,9 +715,9 @@
         PhaseContext phaseContext = new PhaseContext(providers);
 
         // Copy snippet graph, replacing constant parameters with given arguments
-        final StructuredGraph snippetCopy = new StructuredGraph.Builder(options).name(snippetGraph.name).method(snippetGraph.method()).build();
+        final StructuredGraph snippetCopy = new StructuredGraph.Builder(options, debug).name(snippetGraph.name).method(snippetGraph.method()).build();
 
-        try (Debug.Scope scope = Debug.scope("SpecializeSnippet", snippetCopy)) {
+        try (DebugContext.Scope scope = debug.scope("SpecializeSnippet", snippetCopy)) {
             if (!snippetGraph.isUnsafeAccessTrackingEnabled()) {
                 snippetCopy.disableUnsafeAccessTracking();
             }
@@ -748,7 +762,7 @@
             }
             snippetCopy.addDuplicates(snippetGraph.getNodes(), snippetGraph, snippetGraph.getNodeCount(), nodeReplacements);
 
-            Debug.dump(Debug.INFO_LEVEL, snippetCopy, "Before specialization");
+            debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before specialization");
 
             // Gather the template parameters
             parameters = new Object[parameterCount];
@@ -776,10 +790,10 @@
                         for (Node usage : placeholder.usages().snapshot()) {
                             if (usage instanceof LoadIndexedNode) {
                                 LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
-                                Debug.dump(Debug.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
+                                debug.dump(DebugContext.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_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
+                                debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
                             } else if (usage instanceof StoreIndexedNode) {
                                 /*
                                  * The template lowering doesn't really treat this as an array so
@@ -809,10 +823,10 @@
                 new GuardLoweringPhase().apply(snippetCopy, null);
             }
             snippetCopy.setGuardsStage(guardsStage);
-            try (Scope s = Debug.scope("LoweringSnippetTemplate", snippetCopy)) {
+            try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", snippetCopy)) {
                 new LoweringPhase(new CanonicalizerPhase(), args.cacheKey.loweringStage).apply(snippetCopy, phaseContext);
             } catch (Throwable e) {
-                throw Debug.handle(e);
+                throw debug.handle(e);
             }
 
             ArrayList<StateSplit> curSideEffectNodes = new ArrayList<>();
@@ -897,7 +911,7 @@
                     this.memoryAnchor = null;
                 }
             }
-            Debug.dump(Debug.INFO_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
+            debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
 
             List<ReturnNode> returnNodes = snippet.getNodes(ReturnNode.TYPE).snapshot();
             if (returnNodes.isEmpty()) {
@@ -941,11 +955,14 @@
                 }
             }
 
-            Debug.counter("SnippetTemplateNodeCount[%#s]", args).add(nodes.size());
-            Debug.dump(Debug.INFO_LEVEL, snippet, "SnippetTemplate final state");
+            if (debug.areMetricsEnabled()) {
+                DebugContext.counter("SnippetTemplateNodeCount[%#s]", args).add(debug, nodes.size());
+            }
+            debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate final state");
+            this.snippet.freeze();
 
         } catch (Throwable ex) {
-            throw Debug.handle(ex);
+            throw debug.handle(ex);
         }
     }
 
@@ -1065,14 +1082,14 @@
      *
      * @see SnippetInfo#instantiationTimer
      */
-    private final DebugTimer instantiationTimer;
+    private final TimerKey instantiationTimer;
 
     /**
      * Counts instantiations of this template.
      *
      * @see SnippetInfo#instantiationCounter
      */
-    private final DebugCounter instantiationCounter;
+    private final CounterKey instantiationCounter;
 
     /**
      * Gets the instantiation-time bindings to this template's parameters.
@@ -1387,10 +1404,11 @@
      */
     @SuppressWarnings("try")
     public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args, boolean killReplacee) {
+        DebugContext debug = replacee.getDebug();
         assert assertSnippetKills(replacee);
-        try (DebugCloseable a = args.info.instantiationTimer.start(); DebugCloseable b = instantiationTimer.start()) {
-            args.info.instantiationCounter.increment();
-            instantiationCounter.increment();
+        try (DebugCloseable a = args.info.instantiationTimer.start(debug); DebugCloseable b = instantiationTimer.start(debug)) {
+            args.info.instantiationCounter.increment(debug);
+            instantiationCounter.increment(debug);
             // Inline the snippet nodes, replacing parameters with the given args in the process
             StartNode entryPointNode = snippet.start();
             FixedNode firstCFGNode = entryPointNode.next();
@@ -1398,7 +1416,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.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             // Re-wire the control flow graph around the replacee
             FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
@@ -1486,7 +1504,7 @@
                 GraphUtil.killCFG(replacee);
             }
 
-            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
             return duplicates;
         }
     }
@@ -1524,8 +1542,8 @@
     /**
      * Gets a copy of the specialized graph.
      */
-    public StructuredGraph copySpecializedGraph() {
-        return (StructuredGraph) snippet.copy();
+    public StructuredGraph copySpecializedGraph(DebugContext debugForCopy) {
+        return (StructuredGraph) snippet.copy(debugForCopy);
     }
 
     /**
@@ -1539,10 +1557,11 @@
      */
     @SuppressWarnings("try")
     public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, LoweringTool tool, Arguments args) {
+        DebugContext debug = replacee.getDebug();
         assert assertSnippetKills(replacee);
-        try (DebugCloseable a = args.info.instantiationTimer.start()) {
-            args.info.instantiationCounter.increment();
-            instantiationCounter.increment();
+        try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
+            args.info.instantiationCounter.increment(debug);
+            instantiationCounter.increment(debug);
 
             // Inline the snippet nodes, replacing parameters with the given args in the process
             StartNode entryPointNode = snippet.start();
@@ -1551,7 +1570,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.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             FixedWithNextNode lastFixedNode = tool.lastFixedNode();
             assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph + " lastFixed=" + lastFixedNode;
@@ -1575,7 +1594,7 @@
                 returnDuplicate.replaceAndDelete(next);
             }
 
-            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
         }
     }
 
@@ -1591,10 +1610,11 @@
      */
     @SuppressWarnings("try")
     public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, Arguments args) {
+        DebugContext debug = replacee.getDebug();
         assert assertSnippetKills(replacee);
-        try (DebugCloseable a = args.info.instantiationTimer.start()) {
-            args.info.instantiationCounter.increment();
-            instantiationCounter.increment();
+        try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
+            args.info.instantiationCounter.increment(debug);
+            instantiationCounter.increment(debug);
 
             // Inline the snippet nodes, replacing parameters with the given args in the process
             StartNode entryPointNode = snippet.start();
@@ -1613,7 +1633,7 @@
                 }
             }
             UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(floatingNodes, snippet, floatingNodes.size(), replacements);
-            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
+            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
 
             rewireFrameStates(replacee, duplicates);
             updateStamps(replacee, duplicates);
@@ -1625,7 +1645,7 @@
             ValueNode returnValue = (ValueNode) duplicates.get(returnNode.result());
             replacer.replace(replacee, returnValue);
 
-            Debug.dump(Debug.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
+            debug.dump(DebugContext.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/nodes/BasicArrayCopyNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,7 +29,7 @@
 import static org.graalvm.word.LocationIdentity.any;
 
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -218,8 +218,9 @@
                         tool.setVirtualEntry(destVirtual, destPosInt + i, tool.getEntry(srcVirtual, srcPosInt + i), false);
                     }
                     tool.delete();
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("virtualized arraycopyf(%s, %d, %s, %d, %d)", getSource(), srcPosInt, getDestination(), destPosInt, len);
+                    DebugContext debug = getDebug();
+                    if (debug.isLogEnabled()) {
+                        debug.log("virtualized arraycopy(%s, %d, %s, %d, %d)", getSource(), srcPosInt, getDestination(), destPosInt, len);
                     }
                 } else {
                     ResolvedJavaType sourceType = StampTool.typeOrNull(srcAlias);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,15 +22,14 @@
  */
 package org.graalvm.compiler.replacements.nodes;
 
+import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
-import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
 
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.core.common.type.StampPair;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeInputList;
@@ -149,10 +148,11 @@
                 new FrameStateAssignmentPhase().apply(replacementGraph);
             }
         }
-        try (Scope s = Debug.scope("LoweringSnippetTemplate", replacementGraph)) {
+        DebugContext debug = replacementGraph.getDebug();
+        try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", replacementGraph)) {
             new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c);
         } catch (Throwable e) {
-            throw Debug.handle(e);
+            throw debug.handle(e);
         }
         return replacementGraph;
     }
@@ -174,7 +174,7 @@
                 }
             }
             InliningUtil.inline(invoke, replacementGraph, false, targetMethod);
-            Debug.dump(Debug.DETAILED_LEVEL, graph(), "After inlining replacement %s", replacementGraph);
+            replacementGraph.getDebug().dump(DebugContext.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.test/src/org/graalvm/compiler/test/GraalTest.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,12 +22,25 @@
  */
 package org.graalvm.compiler.test;
 
+import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
+import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION;
+import static org.graalvm.compiler.debug.DebugContext.NO_GLOBAL_METRIC_VALUES;
+
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
+import org.graalvm.compiler.debug.DebugHandlersFactory;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpHandler;
+import org.graalvm.compiler.options.OptionValues;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.internal.ComparisonCriteria;
 import org.junit.internal.ExactComparisonCriteria;
@@ -365,4 +378,44 @@
     public static void assertFalse(boolean condition, String message, Object... objects) {
         assertTrue(!condition, message, objects);
     }
+
+    /**
+     * Gets the {@link DebugHandlersFactory}s available for a {@link DebugContext}.
+     */
+    protected Collection<DebugHandlersFactory> getDebugHandlersFactories() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if
+     * none currently exists. Debug contexts created by this method will have their
+     * {@link DebugDumpHandler}s closed in {@link #afterTest()}.
+     */
+    protected DebugContext getDebugContext(OptionValues options) {
+        List<DebugContext> cached = cachedDebugs.get();
+        if (cached == null) {
+            cached = new ArrayList<>();
+            cachedDebugs.set(cached);
+        }
+        for (DebugContext debug : cached) {
+            if (debug.getOptions() == options) {
+                return debug;
+            }
+        }
+        DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, getDebugHandlersFactories());
+        cached.add(debug);
+        return debug;
+    }
+
+    private final ThreadLocal<List<DebugContext>> cachedDebugs = new ThreadLocal<>();
+
+    @After
+    public void afterTest() {
+        List<DebugContext> cached = cachedDebugs.get();
+        if (cached != null) {
+            for (DebugContext debug : cached) {
+                debug.closeDumpHandlers(true);
+            }
+        }
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,7 +27,7 @@
 import java.util.Arrays;
 import java.util.Iterator;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -62,10 +62,15 @@
     private static final Effect[] EMPTY_ARRAY = new Effect[0];
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
+    private final DebugContext debug;
     private Effect[] effects = EMPTY_ARRAY;
     private String[] names = EMPTY_STRING_ARRAY;
     private int size;
 
+    public EffectList(DebugContext debug) {
+        this.debug = debug;
+    }
+
     private void enlarge(int elements) {
         int length = effects.length;
         if (size + elements > length) {
@@ -73,7 +78,7 @@
                 length = Math.max(length * 2, 4);
             }
             effects = Arrays.copyOf(effects, length);
-            if (Debug.isEnabled()) {
+            if (debug.isLogEnabled()) {
                 names = Arrays.copyOf(names, length);
             }
         }
@@ -86,7 +91,7 @@
     public void add(String name, Effect effect) {
         assert effect != null;
         enlarge(1);
-        if (Debug.isEnabled()) {
+        if (debug.isLogEnabled()) {
             names[size] = name;
         }
         effects[size++] = effect;
@@ -95,7 +100,7 @@
     public void addAll(EffectList list) {
         enlarge(list.size);
         System.arraycopy(list.effects, 0, effects, size, list.size);
-        if (Debug.isEnabled()) {
+        if (debug.isLogEnabled()) {
             System.arraycopy(list.names, 0, names, size, list.size);
         }
         size += list.size;
@@ -106,7 +111,7 @@
         enlarge(list.size);
         System.arraycopy(effects, position, effects, position + list.size, size - position);
         System.arraycopy(list.effects, 0, effects, position, list.size);
-        if (Debug.isEnabled()) {
+        if (debug.isLogEnabled()) {
             System.arraycopy(names, position, names, position + list.size, size - position);
             System.arraycopy(list.names, 0, names, position, list.size);
         }
@@ -176,10 +181,10 @@
                     toString(str, i);
                     throw new GraalError(t).addContext("effect", str);
                 }
-                if (effect.isVisible() && Debug.isLogEnabled()) {
+                if (effect.isVisible() && debug.isLogEnabled()) {
                     StringBuilder str = new StringBuilder();
                     toString(str, i);
-                    Debug.log("    %s", str);
+                    debug.log("    %s", str);
                 }
             }
         }
@@ -227,7 +232,7 @@
     }
 
     private String getName(int i) {
-        if (Debug.isEnabled()) {
+        if (debug.isLogEnabled()) {
             return names[i];
         } else {
             return "";
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,7 +28,7 @@
 import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.graph.Node;
@@ -59,10 +59,10 @@
 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator;
 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.LoopInfo;
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
 import org.graalvm.word.LocationIdentity;
-import org.graalvm.util.EconomicMap;
-import org.graalvm.util.EconomicSet;
 
 public abstract class EffectsClosure<BlockT extends EffectsBlockState<BlockT>> extends EffectsPhase.Closure<BlockT> {
 
@@ -112,6 +112,7 @@
     protected final EconomicMap<Loop<Block>, LoopKillCache> loopLocationKillCache = EconomicMap.create(Equivalence.IDENTITY);
 
     protected boolean changed;
+    protected final DebugContext debug;
 
     public EffectsClosure(ScheduleResult schedule, ControlFlowGraph cfg) {
         this.schedule = schedule;
@@ -119,8 +120,9 @@
         this.aliases = cfg.graph.createNodeMap();
         this.hasScalarReplacedInputs = cfg.graph.createNodeBitMap();
         this.blockEffects = new BlockMap<>(cfg);
+        this.debug = cfg.graph.getDebug();
         for (Block block : cfg.getBlocks()) {
-            blockEffects.put(block, new GraphEffectList());
+            blockEffects.put(block, new GraphEffectList(debug));
         }
     }
 
@@ -182,7 +184,7 @@
         };
         ReentrantBlockIterator.apply(closure, cfg.getStartBlock());
         for (GraphEffectList effects : effectList) {
-            Debug.log(" ==== effects");
+            debug.log(" ==== effects");
             effects.apply(graph, obsoleteNodes, false);
         }
         /*
@@ -191,10 +193,10 @@
          * indexes.
          */
         for (GraphEffectList effects : effectList) {
-            Debug.log(" ==== cfg kill effects");
+            debug.log(" ==== cfg kill effects");
             effects.apply(graph, obsoleteNodes, true);
         }
-        Debug.dump(Debug.DETAILED_LEVEL, graph, "After applying effects");
+        debug.dump(DebugContext.DETAILED_LEVEL, graph, "After applying effects");
         assert VirtualUtil.assertNonReachable(graph, obsoleteNodes);
         for (Node node : obsoleteNodes) {
             if (node.isAlive() && node.hasNoUsages()) {
@@ -233,7 +235,7 @@
             }
 
             OptionValues options = block.getBeginNode().getOptions();
-            VirtualUtil.trace(options, "\nBlock: %s, preds: %s, succ: %s (", block, block.getPredecessors(), block.getSuccessors());
+            VirtualUtil.trace(options, debug, "\nBlock: %s, preds: %s, succ: %s (", block, block.getPredecessors(), block.getSuccessors());
 
             // a lastFixedNode is needed in case we want to insert fixed nodes
             FixedWithNextNode lastFixedNode = null;
@@ -257,7 +259,7 @@
                     break;
                 }
             }
-            VirtualUtil.trace(options, ")\n    end state: %s\n", state);
+            VirtualUtil.trace(options, debug, ")\n    end state: %s\n", state);
         }
         return state;
     }
@@ -329,7 +331,7 @@
          * more generic, e.g., adding phis instead of non-phi values.
          */
         for (int iteration = 0; iteration < 10; iteration++) {
-            try (Indent i = Debug.logAndIndent("================== Process Loop Effects Closure: block:%s begin node:%s", loop.getHeader(), loop.getHeader().getBeginNode())) {
+            try (Indent i = debug.logAndIndent("================== Process Loop Effects Closure: block:%s begin node:%s", loop.getHeader(), loop.getHeader().getBeginNode())) {
                 LoopInfo<BlockT> info = ReentrantBlockIterator.processLoop(this, loop, cloneState(lastMergedState));
 
                 List<BlockT> states = new ArrayList<>();
@@ -337,9 +339,9 @@
                 states.addAll(info.endStates);
                 doMergeWithoutDead(mergeProcessor, states);
 
-                Debug.log("MergeProcessor New State: %s", mergeProcessor.newState);
-                Debug.log("===== vs.");
-                Debug.log("Last Merged State: %s", lastMergedState);
+                debug.log("MergeProcessor New State: %s", mergeProcessor.newState);
+                debug.log("===== vs.");
+                debug.log("Last Merged State: %s", lastMergedState);
 
                 if (mergeProcessor.newState.equivalentTo(lastMergedState)) {
                     blockEffects.get(loop.getHeader()).insertAll(mergeProcessor.mergeEffects, 0);
@@ -441,8 +443,8 @@
         public MergeProcessor(Block mergeBlock) {
             this.mergeBlock = mergeBlock;
             this.merge = (AbstractMergeNode) mergeBlock.getBeginNode();
-            this.mergeEffects = new GraphEffectList();
-            this.afterMergeEffects = new GraphEffectList();
+            this.mergeEffects = new GraphEffectList(debug);
+            this.afterMergeEffects = new GraphEffectList(debug);
         }
 
         /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -22,12 +22,10 @@
  */
 package org.graalvm.compiler.virtual.phases.ea;
 
-import static org.graalvm.compiler.debug.Debug.isEnabled;
 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
 
 import org.graalvm.compiler.core.common.util.CompilationAlarm;
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.Debug.Scope;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph.NodeEventScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.spi.Simplifiable;
@@ -77,8 +75,9 @@
     public boolean runAnalysis(StructuredGraph graph, PhaseContextT context) {
         boolean changed = false;
         CompilationAlarm compilationAlarm = CompilationAlarm.current();
+        DebugContext debug = graph.getDebug();
         for (int iteration = 0; iteration < maxIterations && !compilationAlarm.hasExpired(); iteration++) {
-            try (Scope s = Debug.scope(isEnabled() ? "iteration " + iteration : null)) {
+            try (DebugContext.Scope s = debug.scope(debug.areScopesEnabled() ? "iteration " + iteration : null)) {
                 ScheduleResult schedule;
                 ControlFlowGraph cfg;
                 if (unscheduled) {
@@ -89,7 +88,7 @@
                     schedule = graph.getLastSchedule();
                     cfg = schedule.getCFG();
                 }
-                try (Scope scheduleScope = Debug.scope("EffectsPhaseWithSchedule", schedule)) {
+                try (DebugContext.Scope scheduleScope = debug.scope("EffectsPhaseWithSchedule", schedule)) {
                     Closure<?> closure = createEffectsClosure(context, schedule, cfg);
                     ReentrantBlockIterator.apply(closure, cfg.getStartBlock());
 
@@ -100,8 +99,8 @@
                             closure.applyEffects();
                         }
 
-                        if (Debug.isDumpEnabled(Debug.INFO_LEVEL)) {
-                            Debug.dump(Debug.DETAILED_LEVEL, graph, "%s iteration", getName());
+                        if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
+                            debug.dump(DebugContext.DETAILED_LEVEL, graph, "%s iteration", getName());
                         }
 
                         new DeadCodeEliminationPhase(Required).apply(graph);
@@ -121,7 +120,7 @@
                         break;
                     }
                 } catch (Throwable t) {
-                    throw Debug.handle(t);
+                    throw debug.handle(t);
                 }
             }
         }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,6 +24,7 @@
 
 import java.util.ArrayList;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ControlSinkNode;
 import org.graalvm.compiler.nodes.FixedNode;
@@ -43,6 +44,10 @@
 
 public final class GraphEffectList extends EffectList {
 
+    public GraphEffectList(DebugContext debug) {
+        super(debug);
+    }
+
     /**
      * Determines how many objects are virtualized (positive) or materialized (negative) by this
      * effect.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,8 +25,8 @@
 import java.util.Arrays;
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MonitorIdNode;
 import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
@@ -44,8 +44,8 @@
  */
 public class ObjectState {
 
-    public static final DebugCounter CREATE_ESCAPED_OBJECT_STATE = Debug.counter("CreateEscapeObjectState");
-    public static final DebugCounter GET_ESCAPED_OBJECT_STATE = Debug.counter("GetEscapeObjectState");
+    public static final CounterKey CREATE_ESCAPED_OBJECT_STATE = DebugContext.counter("CreateEscapeObjectState");
+    public static final CounterKey GET_ESCAPED_OBJECT_STATE = DebugContext.counter("GetEscapeObjectState");
 
     private ValueNode[] entries;
     private ValueNode materializedValue;
@@ -92,10 +92,10 @@
         return new ObjectState(this);
     }
 
-    public EscapeObjectState createEscapeObjectState(VirtualObjectNode virtual) {
-        GET_ESCAPED_OBJECT_STATE.increment();
+    public EscapeObjectState createEscapeObjectState(DebugContext debug, VirtualObjectNode virtual) {
+        GET_ESCAPED_OBJECT_STATE.increment(debug);
         if (cachedState == null) {
-            CREATE_ESCAPED_OBJECT_STATE.increment();
+            CREATE_ESCAPED_OBJECT_STATE.increment(debug);
             if (isVirtual()) {
                 /*
                  * Clear out entries that are default values anyway.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -25,6 +25,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
@@ -73,8 +74,8 @@
         }
     }
 
-    public PEReadEliminationBlockState(OptionValues options) {
-        super(options);
+    public PEReadEliminationBlockState(OptionValues options, DebugContext debug) {
+        super(options, debug);
         readCache = EconomicMap.create(Equivalence.DEFAULT);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java	Fri Jul 07 09:40:47 2017 -0700
@@ -31,8 +31,8 @@
 
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
@@ -45,9 +45,9 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValueProxyNode;
 import org.graalvm.compiler.nodes.cfg.Block;
-import org.graalvm.compiler.nodes.extended.UnboxNode;
 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.java.ArrayLengthNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
@@ -91,7 +91,7 @@
 
     @Override
     protected PEReadEliminationBlockState getInitialState() {
-        return new PEReadEliminationBlockState(tool.getOptions());
+        return new PEReadEliminationBlockState(tool.getOptions(), tool.getDebug());
     }
 
     @Override
@@ -117,11 +117,11 @@
         } else if (node instanceof RawStoreNode) {
             return processUnsafeStore((RawStoreNode) node, state, effects);
         } else if (node instanceof MemoryCheckpoint.Single) {
-            COUNTER_MEMORYCHECKPOINT.increment();
+            COUNTER_MEMORYCHECKPOINT.increment(node.getDebug());
             LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
             processIdentity(state, identity);
         } else if (node instanceof MemoryCheckpoint.Multi) {
-            COUNTER_MEMORYCHECKPOINT.increment();
+            COUNTER_MEMORYCHECKPOINT.increment(node.getDebug());
             for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
                 processIdentity(state, identity);
             }
@@ -426,7 +426,8 @@
                 loopKilledLocations = new LoopKillCache(1/* 1.visit */);
                 loopLocationKillCache.put(loop, loopKilledLocations);
             } else {
-                OptionValues options = loop.getHeader().getBeginNode().getOptions();
+                AbstractBeginNode beginNode = loop.getHeader().getBeginNode();
+                OptionValues options = beginNode.getOptions();
                 if (loopKilledLocations.visits() > ReadEliminationMaxLoopVisits.getValue(options)) {
                     // we have processed the loop too many times, kill all locations so the inner
                     // loop will never be processed more than once again on visit
@@ -445,9 +446,9 @@
                     for (LocationIdentity location : forwardEndLiveLocations) {
                         loopKilledLocations.rememberLoopKilledLocation(location);
                     }
-                    if (Debug.isLogEnabled() && loopKilledLocations != null) {
-                        Debug.log("[Early Read Elimination] Setting loop killed locations of loop at node %s with %s",
-                                        loop.getHeader().getBeginNode(), forwardEndLiveLocations);
+                    if (debug.isLogEnabled() && loopKilledLocations != null) {
+                        debug.log("[Early Read Elimination] Setting loop killed locations of loop at node %s with %s",
+                                        beginNode, forwardEndLiveLocations);
                     }
                 }
                 // remember the loop visit
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java	Fri Jul 07 09:40:47 2017 -0700
@@ -28,6 +28,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
@@ -63,6 +64,7 @@
     private RefCount arrayRefCount;
 
     private final OptionValues options;
+    private final DebugContext debug;
 
     /**
      * Final subclass of PartialEscapeBlockState, for performance and to make everything behave
@@ -70,8 +72,8 @@
      */
     public static final class Final extends PartialEscapeBlockState<Final> {
 
-        public Final(OptionValues options) {
-            super(options);
+        public Final(OptionValues options, DebugContext debug) {
+            super(options, debug);
         }
 
         public Final(Final other) {
@@ -79,16 +81,18 @@
         }
     }
 
-    protected PartialEscapeBlockState(OptionValues options) {
+    protected PartialEscapeBlockState(OptionValues options, DebugContext debug) {
         objectStates = EMPTY_ARRAY;
         arrayRefCount = new RefCount();
         this.options = options;
+        this.debug = debug;
     }
 
     protected PartialEscapeBlockState(PartialEscapeBlockState<T> other) {
         super(other);
         adoptAddObjectStates(other);
         options = other.options;
+        debug = other.debug;
     }
 
     public ObjectState getObjectState(int object) {
@@ -169,14 +173,13 @@
      * entries.
      */
     public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
-        PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment();
+        PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment(fixed.getDebug());
         List<AllocatedObjectNode> objects = new ArrayList<>(2);
         List<ValueNode> values = new ArrayList<>(8);
         List<List<MonitorIdNode>> locks = new ArrayList<>();
         List<ValueNode> otherAllocations = new ArrayList<>(2);
         List<Boolean> ensureVirtual = new ArrayList<>(2);
         materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations);
-        assert fixed != null;
 
         materializeEffects.addVirtualizationDelta(-(objects.size() + otherAllocations.size()));
         materializeEffects.add("materializeBefore", new Effect() {
@@ -255,14 +258,14 @@
             }
             objectMaterialized(virtual, (AllocatedObjectNode) representation, values.subList(pos, pos + entries.length));
         } else {
-            VirtualUtil.trace(options, "materialized %s as %s", virtual, representation);
+            VirtualUtil.trace(options, debug, "materialized %s as %s", virtual, representation);
             otherAllocations.add(representation);
             assert obj.getLocks() == null;
         }
     }
 
     protected void objectMaterialized(VirtualObjectNode virtual, AllocatedObjectNode representation, List<ValueNode> values) {
-        VirtualUtil.trace(options, "materialized %s as %s with values %s", virtual, representation, values);
+        VirtualUtil.trace(options, debug, "materialized %s as %s with values %s", virtual, representation, values);
     }
 
     public void addObject(int virtual, ObjectState state) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java	Fri Jul 07 09:40:47 2017 -0700
@@ -33,8 +33,8 @@
 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.debug.Debug;
-import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
 import org.graalvm.compiler.graph.Position;
@@ -78,14 +78,14 @@
 
 public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockState<BlockT>> extends EffectsClosure<BlockT> {
 
-    public static final DebugCounter COUNTER_MATERIALIZATIONS = Debug.counter("Materializations");
-    public static final DebugCounter COUNTER_MATERIALIZATIONS_PHI = Debug.counter("MaterializationsPhi");
-    public static final DebugCounter COUNTER_MATERIALIZATIONS_MERGE = Debug.counter("MaterializationsMerge");
-    public static final DebugCounter COUNTER_MATERIALIZATIONS_UNHANDLED = Debug.counter("MaterializationsUnhandled");
-    public static final DebugCounter COUNTER_MATERIALIZATIONS_LOOP_REITERATION = Debug.counter("MaterializationsLoopReiteration");
-    public static final DebugCounter COUNTER_MATERIALIZATIONS_LOOP_END = Debug.counter("MaterializationsLoopEnd");
-    public static final DebugCounter COUNTER_ALLOCATION_REMOVED = Debug.counter("AllocationsRemoved");
-    public static final DebugCounter COUNTER_MEMORYCHECKPOINT = Debug.counter("MemoryCheckpoint");
+    public static final CounterKey COUNTER_MATERIALIZATIONS = DebugContext.counter("Materializations");
+    public static final CounterKey COUNTER_MATERIALIZATIONS_PHI = DebugContext.counter("MaterializationsPhi");
+    public static final CounterKey COUNTER_MATERIALIZATIONS_MERGE = DebugContext.counter("MaterializationsMerge");
+    public static final CounterKey COUNTER_MATERIALIZATIONS_UNHANDLED = DebugContext.counter("MaterializationsUnhandled");
+    public static final CounterKey COUNTER_MATERIALIZATIONS_LOOP_REITERATION = DebugContext.counter("MaterializationsLoopReiteration");
+    public static final CounterKey COUNTER_MATERIALIZATIONS_LOOP_END = DebugContext.counter("MaterializationsLoopEnd");
+    public static final CounterKey COUNTER_ALLOCATION_REMOVED = DebugContext.counter("AllocationsRemoved");
+    public static final CounterKey COUNTER_MEMORYCHECKPOINT = DebugContext.counter("MemoryCheckpoint");
 
     /**
      * Nodes with inputs that were modified during analysis are marked in this bitset - this way
@@ -102,6 +102,7 @@
      * The indexes into this array correspond to {@link VirtualObjectNode#getObjectId()}.
      */
     public final ArrayList<VirtualObjectNode> virtualObjects = new ArrayList<>();
+    public final DebugContext debug;
 
     @Override
     public boolean needsApplyEffects() {
@@ -171,7 +172,7 @@
 
         @Override
         protected PartialEscapeBlockState.Final getInitialState() {
-            return new PartialEscapeBlockState.Final(tool.getOptions());
+            return new PartialEscapeBlockState.Final(tool.getOptions(), tool.getDebug());
         }
 
         @Override
@@ -189,7 +190,8 @@
         super(schedule, schedule.getCFG());
         StructuredGraph graph = schedule.getCFG().graph;
         this.hasVirtualInputs = graph.createNodeBitMap();
-        this.tool = new VirtualizerToolImpl(metaAccess, constantReflection, constantFieldProvider, this, graph.getAssumptions(), graph.getOptions(), loweringProvider);
+        this.debug = graph.getDebug();
+        this.tool = new VirtualizerToolImpl(metaAccess, constantReflection, constantFieldProvider, this, graph.getAssumptions(), graph.getOptions(), debug, loweringProvider);
     }
 
     /**
@@ -212,14 +214,14 @@
 
     private boolean processNodeInternal(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode) {
         FixedNode nextFixedNode = lastFixedNode == null ? null : lastFixedNode.next();
-        VirtualUtil.trace(node.getOptions(), "%s", node);
+        VirtualUtil.trace(node.getOptions(), debug, "%s", node);
 
         if (requiresProcessing(node)) {
             if (processVirtualizable((ValueNode) node, nextFixedNode, state, effects) == false) {
                 return false;
             }
             if (tool.isDeleted()) {
-                VirtualUtil.trace(node.getOptions(), "deleted virtualizable allocation %s", node);
+                VirtualUtil.trace(node.getOptions(), debug, "deleted virtualizable allocation %s", node);
                 return true;
             }
         }
@@ -229,7 +231,7 @@
                     return false;
                 }
                 if (tool.isDeleted()) {
-                    VirtualUtil.trace(node.getOptions(), "deleted virtualizable node %s", node);
+                    VirtualUtil.trace(node.getOptions(), debug, "deleted virtualizable node %s", node);
                     return true;
                 }
             }
@@ -296,7 +298,7 @@
                 }
             } else {
                 if (!prepareCanonicalNode(canonicalizedValue, state, effects)) {
-                    VirtualUtil.trace(node.getOptions(), "replacement via canonicalization too complex: %s -> %s", node, canonicalizedValue);
+                    VirtualUtil.trace(node.getOptions(), debug, "replacement via canonicalization too complex: %s -> %s", node, canonicalizedValue);
                     return false;
                 }
                 if (canonicalizedValue instanceof ControlSinkNode) {
@@ -307,7 +309,7 @@
                     addScalarAlias(node, canonicalizedValue);
                 }
             }
-            VirtualUtil.trace(node.getOptions(), "replaced via canonicalization: %s -> %s", node, canonicalizedValue);
+            VirtualUtil.trace(node.getOptions(), debug, "replaced via canonicalization: %s -> %s", node, canonicalizedValue);
             return true;
         }
         return false;
@@ -350,7 +352,7 @@
      * {@link VirtualObjectState}.
      */
     private void processNodeInputs(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) {
-        VirtualUtil.trace(node.getOptions(), "processing nodewithstate: %s", node);
+        VirtualUtil.trace(node.getOptions(), debug, "processing nodewithstate: %s", node);
         for (Node input : node.inputs()) {
             if (input instanceof ValueNode) {
                 ValueNode alias = getAlias((ValueNode) input);
@@ -358,7 +360,7 @@
                     int id = ((VirtualObjectNode) alias).getObjectId();
                     ensureMaterialized(state, id, insertBefore, effects, COUNTER_MATERIALIZATIONS_UNHANDLED);
                     effects.replaceFirstInput(node, input, state.getObjectState(id).getMaterializedValue());
-                    VirtualUtil.trace(node.getOptions(), "replacing input %s at %s", input, node);
+                    VirtualUtil.trace(node.getOptions(), debug, "replacing input %s at %s", input, node);
                 }
             }
         }
@@ -390,7 +392,7 @@
 
     private void addVirtualMappings(FrameState frameState, EconomicSet<VirtualObjectNode> virtual, BlockT state, GraphEffectList effects) {
         for (VirtualObjectNode obj : virtual) {
-            effects.addVirtualMapping(frameState, state.getObjectState(obj).createEscapeObjectState(obj));
+            effects.addVirtualMapping(frameState, state.getObjectState(obj).createEscapeObjectState(debug, obj));
         }
     }
 
@@ -427,9 +429,9 @@
     /**
      * @return true if materialization happened, false if not.
      */
-    protected boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, DebugCounter counter) {
+    protected boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, CounterKey counter) {
         if (state.getObjectState(object).isVirtual()) {
-            counter.increment();
+            counter.increment(debug);
             VirtualObjectNode virtual = virtualObjects.get(object);
             state.materializeBefore(materializeBefore, virtual, effects);
             assert !updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()) : "method must already have been called before";
@@ -566,7 +568,7 @@
             exitState.updateMaterializedValue(object, proxy);
         } else {
             if (initialObjState.getMaterializedValue() != exitObjState.getMaterializedValue()) {
-                Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObjState.getMaterializedValue(), exitObjState.getMaterializedValue(), exitNode);
+                exitNode.getDebug().log("materialized value changes within loop: %s vs. %s at %s", initialObjState.getMaterializedValue(), exitObjState.getMaterializedValue(), exitNode);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java	Fri Jul 07 09:40:47 2017 -0700
@@ -30,7 +30,6 @@
 
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.debug.Debug;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
@@ -44,9 +43,9 @@
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
 import org.graalvm.compiler.nodes.extended.GuardedNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
-import org.graalvm.compiler.nodes.extended.UnsafeAccessNode;
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.extended.RawStoreNode;
+import org.graalvm.compiler.nodes.extended.UnsafeAccessNode;
 import org.graalvm.compiler.nodes.java.AccessFieldNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
@@ -58,9 +57,9 @@
 import org.graalvm.compiler.virtual.phases.ea.ReadEliminationBlockState.CacheEntry;
 import org.graalvm.compiler.virtual.phases.ea.ReadEliminationBlockState.LoadCacheEntry;
 import org.graalvm.compiler.virtual.phases.ea.ReadEliminationBlockState.UnsafeLoadCacheEntry;
-import org.graalvm.util.Equivalence;
 import org.graalvm.util.EconomicMap;
 import org.graalvm.util.EconomicSet;
+import org.graalvm.util.Equivalence;
 import org.graalvm.util.MapCursor;
 import org.graalvm.word.LocationIdentity;
 
@@ -359,8 +358,8 @@
                     for (LocationIdentity location : forwardEndLiveLocations) {
                         loopKilledLocations.rememberLoopKilledLocation(location);
                     }
-                    if (Debug.isLogEnabled() && loopKilledLocations != null) {
-                        Debug.log("[Early Read Elimination] Setting loop killed locations of loop at node %s with %s",
+                    if (debug.isLogEnabled() && loopKilledLocations != null) {
+                        debug.log("[Early Read Elimination] Setting loop killed locations of loop at node %s with %s",
                                         loop.getHeader().getBeginNode(), forwardEndLiveLocations);
                     }
                 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualUtil.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualUtil.java	Fri Jul 07 09:40:47 2017 -0700
@@ -26,7 +26,7 @@
 
 import java.util.List;
 
-import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.TTY;
 import org.graalvm.compiler.graph.Node;
@@ -51,6 +51,7 @@
         // Nodes with support for GVN can be kept alive by GVN and are therefore not part of the
         // assertion.
 
+        DebugContext debug = graph.getDebug();
         NodeFlood flood = graph.createNodeFlood();
         EconomicMap<Node, Node> path = EconomicMap.create(Equivalence.IDENTITY);
         flood.add(graph.start());
@@ -115,38 +116,38 @@
         }
         if (!success) {
             TTY.println();
-            Debug.forceDump(graph, "assertNonReachable");
+            debug.forceDump(graph, "assertNonReachable");
         }
         return success;
     }
 
-    public static void trace(OptionValues options, String msg) {
-        if (Debug.isEnabled() && TraceEscapeAnalysis.getValue(options) && Debug.isLogEnabled()) {
-            Debug.log(msg);
+    public static void trace(OptionValues options, DebugContext debug, String msg) {
+        if (debug.areScopesEnabled() && TraceEscapeAnalysis.getValue(options) && debug.isLogEnabled()) {
+            debug.log(msg);
         }
     }
 
-    public static void trace(OptionValues options, String format, Object obj) {
-        if (Debug.isEnabled() && TraceEscapeAnalysis.getValue(options) && Debug.isLogEnabled()) {
-            Debug.logv(format, obj);
+    public static void trace(OptionValues options, DebugContext debug, String format, Object obj) {
+        if (debug.areScopesEnabled() && TraceEscapeAnalysis.getValue(options) && debug.isLogEnabled()) {
+            debug.logv(format, obj);
         }
     }
 
-    public static void trace(OptionValues options, String format, Object obj, Object obj2) {
-        if (Debug.isEnabled() && TraceEscapeAnalysis.getValue(options) && Debug.isLogEnabled()) {
-            Debug.logv(format, obj, obj2);
+    public static void trace(OptionValues options, DebugContext debug, String format, Object obj, Object obj2) {
+        if (debug.areScopesEnabled() && TraceEscapeAnalysis.getValue(options) && debug.isLogEnabled()) {
+            debug.logv(format, obj, obj2);
         }
     }
 
-    public static void trace(OptionValues options, String format, Object obj, Object obj2, Object obj3) {
-        if (Debug.isEnabled() && TraceEscapeAnalysis.getValue(options) && Debug.isLogEnabled()) {
-            Debug.logv(format, obj, obj2, obj3);
+    public static void trace(OptionValues options, DebugContext debug, String format, Object obj, Object obj2, Object obj3) {
+        if (debug.areScopesEnabled() && TraceEscapeAnalysis.getValue(options) && debug.isLogEnabled()) {
+            debug.logv(format, obj, obj2, obj3);
         }
     }
 
-    public static void trace(OptionValues options, String format, Object obj, Object obj2, Object obj3, Object obj4) {
-        if (Debug.isEnabled() && TraceEscapeAnalysis.getValue(options) && Debug.isLogEnabled()) {
-            Debug.logv(format, obj, obj2, obj3, obj4);
+    public static void trace(OptionValues options, DebugContext debug, String format, Object obj, Object obj2, Object obj3, Object obj4) {
+        if (debug.areScopesEnabled() && TraceEscapeAnalysis.getValue(options) && debug.isLogEnabled()) {
+            debug.logv(format, obj, obj2, obj3, obj4);
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java	Fri Jul 07 09:40:47 2017 -0700
@@ -27,6 +27,7 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodes.FixedNode;
@@ -55,16 +56,18 @@
     private final PartialEscapeClosure<?> closure;
     private final Assumptions assumptions;
     private final OptionValues options;
+    private final DebugContext debug;
     private final LoweringProvider loweringProvider;
 
     VirtualizerToolImpl(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, PartialEscapeClosure<?> closure,
-                    Assumptions assumptions, OptionValues options, LoweringProvider loweringProvider) {
+                    Assumptions assumptions, OptionValues options, DebugContext debug, LoweringProvider loweringProvider) {
         this.metaAccess = metaAccess;
         this.constantReflection = constantReflection;
         this.constantFieldProvider = constantFieldProvider;
         this.closure = closure;
         this.assumptions = assumptions;
         this.options = options;
+        this.debug = debug;
         this.loweringProvider = loweringProvider;
     }
 
@@ -80,6 +83,11 @@
     }
 
     @Override
+    public DebugContext getDebug() {
+        return debug;
+    }
+
+    @Override
     public MetaAccessProvider getMetaAccessProvider() {
         return metaAccess;
     }
@@ -181,7 +189,7 @@
 
     @Override
     public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, List<MonitorIdNode> locks, boolean ensureVirtualized) {
-        VirtualUtil.trace(options, "{{%s}} ", current);
+        VirtualUtil.trace(options, debug, "{{%s}} ", current);
         if (!virtualObject.isAlive()) {
             effects.addFloatingNode(virtualObject, "newVirtualObject");
         }
@@ -197,7 +205,7 @@
         }
         state.addObject(id, new ObjectState(entryState, locks, ensureVirtualized));
         closure.addVirtualAlias(virtualObject, virtualObject);
-        PartialEscapeClosure.COUNTER_ALLOCATION_REMOVED.increment();
+        PartialEscapeClosure.COUNTER_ALLOCATION_REMOVED.increment(debug);
         effects.addVirtualizationDelta(1);
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/Word.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/Word.java	Fri Jul 07 09:40:47 2017 -0700
@@ -80,6 +80,8 @@
     public enum Opcode {
         NODE_CLASS,
         COMPARISON,
+        IS_NULL,
+        IS_NON_NULL,
         NOT,
         READ_POINTER,
         READ_OBJECT,
@@ -426,6 +428,18 @@
     }
 
     @Override
+    @Operation(opcode = Opcode.IS_NULL)
+    public boolean isNull() {
+        return equal(WordFactory.zero());
+    }
+
+    @Override
+    @Operation(opcode = Opcode.IS_NON_NULL)
+    public boolean isNonNull() {
+        return notEqual(WordFactory.zero());
+    }
+
+    @Override
     @Operation(opcode = Opcode.COMPARISON, condition = Condition.EQ)
     public boolean equal(ComparableWord val) {
         return equal((Word) val);
@@ -716,7 +730,7 @@
     @Override
     @Operation(opcode = Opcode.READ_POINTER)
     public <T extends WordBase> T readWord(int offset, LocationIdentity locationIdentity) {
-        return readWord((WordBase)signed(offset), locationIdentity);
+        return readWord(signed(offset), locationIdentity);
     }
 
     @Override
@@ -943,7 +957,7 @@
     @Override
     @Operation(opcode = Opcode.READ_POINTER)
     public <T extends WordBase> T readWord(int offset) {
-        return readWord((WordBase)signed(offset));
+        return readWord(signed(offset));
     }
 
     @Override
@@ -1116,7 +1130,7 @@
     @Override
     @Operation(opcode = Opcode.CAS_POINTER)
     public <T extends WordBase> T compareAndSwapWord(int offset, T expectedValue, T newValue, LocationIdentity locationIdentity) {
-        return compareAndSwapWord((WordBase)signed(offset), expectedValue, newValue, locationIdentity);
+        return compareAndSwapWord(signed(offset), expectedValue, newValue, locationIdentity);
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordOperationPlugin.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordOperationPlugin.java	Fri Jul 07 09:40:47 2017 -0700
@@ -282,6 +282,16 @@
                 b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1])));
                 break;
 
+            case IS_NULL:
+                assert args.length == 1;
+                b.push(returnKind, comparisonOp(b, Condition.EQ, args[0], ConstantNode.forIntegerKind(wordKind, 0L)));
+                break;
+
+            case IS_NON_NULL:
+                assert args.length == 1;
+                b.push(returnKind, comparisonOp(b, Condition.NE, args[0], ConstantNode.forIntegerKind(wordKind, 0L)));
+                break;
+
             case NOT:
                 assert args.length == 1;
                 b.addPush(returnKind, new XorNode(args[0], b.add(forIntegerKind(wordKind, -1))));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java	Fri Jul 07 09:40:47 2017 -0700
@@ -24,6 +24,8 @@
  */
 package org.graalvm.options;
 
+import java.util.Objects;
+
 /**
  * Represents meta-data for a single option.
  *
@@ -92,6 +94,8 @@
     }
 
     /**
+     * {@inheritDoc}
+     *
      * @since 1.0
      */
     @Override
@@ -100,12 +104,53 @@
     }
 
     /**
+     * {@inheritDoc}
+     *
+     * @since 1.0
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (deprecated ? 1231 : 1237);
+        result = prime * result + ((help == null) ? 0 : help.hashCode());
+        result = prime * result + ((key == null) ? 0 : key.hashCode());
+        result = prime * result + ((kind == null) ? 0 : kind.hashCode());
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 1.0
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj == null) {
+            return false;
+        } else if (getClass() != obj.getClass()) {
+            return false;
+        }
+        OptionDescriptor other = (OptionDescriptor) obj;
+        return Objects.equals(name, other.name) &&
+                        Objects.equals(deprecated, other.deprecated) &&
+                        Objects.equals(help, other.help) &&
+                        Objects.equals(key, other.key) &&
+                        Objects.equals(kind, other.kind);
+    }
+
+    /**
      * Creates a new option descriptor builder by key. The option group and name is inferred by the
      * key.
      *
      * @since 1.0
      */
     public static <T> Builder newBuilder(OptionKey<T> key, String name) {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(name);
         return new Builder(key, name);
     }
 
@@ -134,6 +179,7 @@
          * @since 1.0
          */
         public Builder category(@SuppressWarnings("hiding") OptionCategory category) {
+            Objects.requireNonNull(category);
             this.category = category;
             return this;
         }
@@ -155,6 +201,7 @@
          * @since 1.0
          */
         public Builder help(@SuppressWarnings("hiding") String help) {
+            Objects.requireNonNull(help);
             this.help = help;
             return this;
         }
@@ -165,7 +212,7 @@
          * @since 1.0
          */
         public OptionDescriptor build() {
-            return new OptionDescriptor(key, name, help, category, deprecated);
+            return new OptionDescriptor(key, name, help == null ? "" : help, category == null ? OptionCategory.DEBUG : category, deprecated);
         }
 
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptors.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptors.java	Fri Jul 07 09:40:47 2017 -0700
@@ -81,6 +81,14 @@
     }
 
     /**
+     * {@inheritDoc}
+     *
+     * @since 1.0
+     */
+    @Override
+    Iterator<OptionDescriptor> iterator();
+
+    /**
      * Create an {@link OptionDescriptors} instance from a list. The option descriptors
      * implementation is backed by a {@link LinkedHashMap} that preserves ordering.
      *
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerBase.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerBase.java	Fri Jul 07 09:40:47 2017 -0700
@@ -29,4 +29,14 @@
  * necessarily all the memory access methods defined in {@link Pointer}).
  */
 public interface PointerBase extends ComparableWord {
+
+    /**
+     * Returns true if this pointer is the {@link WordFactory#nullPointer null pointer}.
+     */
+    boolean isNull();
+
+    /**
+     * Returns true if this pointer is not the {@link WordFactory#nullPointer null pointer}.
+     */
+    boolean isNonNull();
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerUtils.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/PointerUtils.java	Fri Jul 07 09:40:47 2017 -0700
@@ -38,9 +38,9 @@
      *
      * @return A null Pointer value.
      */
-    @SuppressWarnings("unchecked")
     public static <T extends PointerBase> T nullPointer() {
-        return (T) WordFactory.zero();
+        /* This method will be deleted soon. */
+        return WordFactory.nullPointer();
     }
 
     /**
@@ -49,7 +49,8 @@
      * @return Whether that Pointer is the null Pointer.
      */
     public static boolean isNull(ComparableWord that) {
-        return that.equal(nullPointer());
+        /* This method will be deleted soon. */
+        return ((PointerBase) that).isNull();
     }
 
     /**
@@ -58,7 +59,8 @@
      * @return Whether that Pointer is not the null Pointer.
      */
     public static boolean isNonNull(ComparableWord that) {
-        return that.notEqual(nullPointer());
+        /* This method will be deleted soon. */
+        return ((PointerBase) that).isNonNull();
     }
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/WordFactory.java	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/src/org/graalvm/word/WordFactory.java	Fri Jul 07 09:40:47 2017 -0700
@@ -87,6 +87,17 @@
     }
 
     /**
+     * The null pointer, i.e., the pointer with no bits set. There is no difference to a signed or
+     * unsigned {@link #zero}.
+     *
+     * @return the null pointer.
+     */
+    @FactoryOperation(opcode = FactoryOpcode.ZERO)
+    public static <T extends PointerBase> T nullPointer() {
+        return boxFactory.box(0L);
+    }
+
+    /**
      * Unsafe conversion from a Java long value to a Word. The parameter is treated as an unsigned
      * 64-bit value (in contrast to the semantics of a Java long).
      *
--- a/hotspot/test/compiler/aot/scripts/build-bootmodules.sh	Fri Jul 07 10:37:52 2017 +0200
+++ b/hotspot/test/compiler/aot/scripts/build-bootmodules.sh	Fri Jul 07 09:40:47 2017 -0700
@@ -36,7 +36,7 @@
 
 $JAVA_HOME/bin/javac -d . $DIR/$TEST.java
 
-JAOTC_OPTS="-J-Xmx4g -J-ea --compile-for-tiered --info"
+JAOTC_OPTS="-J-Xmx4g --compile-for-tiered --info"
 JAVA_OPTS="-Xmx4g -XX:+UseAOT -XX:+UnlockDiagnosticVMOptions -XX:+UseAOTStrictLoading"
 
 # Compile with: +UseCompressedOops +UseG1GC